Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
atlas
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
dataplatform
atlas
Commits
5783b701
Commit
5783b701
authored
Mar 14, 2020
by
Madhan Neethiraj
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ATLAS-3666: updated file-based authentication to use BCrypt
parent
f1df13dc
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
184 additions
and
85 deletions
+184
-85
CredentialProviderUtility.java
...java/org/apache/atlas/util/CredentialProviderUtility.java
+44
-37
UserDao.java
webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java
+117
-20
AtlasFileAuthenticationProvider.java
...e/atlas/web/security/AtlasFileAuthenticationProvider.java
+10
-28
FileAuthenticationTest.java
...org/apache/atlas/web/security/FileAuthenticationTest.java
+13
-0
No files found.
webapp/src/main/java/org/apache/atlas/util/CredentialProviderUtility.java
View file @
5783b701
...
...
@@ -17,17 +17,15 @@
package
org
.
apache
.
atlas
.
util
;
import
org.apache.atlas.web.dao.UserDao
;
import
org.apache.commons.cli.BasicParser
;
import
org.apache.commons.cli.CommandLine
;
import
org.apache.commons.cli.DefaultParser
;
import
org.apache.commons.cli.Options
;
import
org.apache.commons.lang.StringUtils
;
import
org.apache.hadoop.conf.Configuration
;
import
org.apache.hadoop.security.alias.CredentialProvider
;
import
org.apache.hadoop.security.alias.CredentialProviderFactory
;
import
org.apache.hadoop.security.alias.JavaKeyStoreProvider
;
import
java.io.Console
;
import
java.io.File
;
import
java.io.IOException
;
import
java.util.Arrays
;
...
...
@@ -41,8 +39,7 @@ import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_PASSWORD_K
* of the DGC server.
*/
public
class
CredentialProviderUtility
{
private
static
final
String
[]
KEYS
=
new
String
[]{
KEYSTORE_PASSWORD_KEY
,
TRUSTSTORE_PASSWORD_KEY
,
SERVER_CERT_PASSWORD_KEY
};
private
static
final
String
[]
KEYS
=
new
String
[]
{
KEYSTORE_PASSWORD_KEY
,
TRUSTSTORE_PASSWORD_KEY
,
SERVER_CERT_PASSWORD_KEY
};
public
static
abstract
class
TextDevice
{
public
abstract
void
printf
(
String
fmt
,
Object
...
params
);
...
...
@@ -75,13 +72,8 @@ public class CredentialProviderUtility {
public
static
TextDevice
textDevice
=
DEFAULT_TEXT_DEVICE
;
public
static
void
main
(
String
[]
args
)
throws
IOException
{
Options
options
=
new
Options
();
try
{
createOptions
(
options
);
CommandLine
cmd
=
new
BasicParser
().
parse
(
options
,
args
);
CommandLine
cmd
=
new
DefaultParser
().
parse
(
createOptions
(),
args
);
boolean
generatePasswordOption
=
cmd
.
hasOption
(
"g"
);
if
(
generatePasswordOption
)
{
...
...
@@ -89,20 +81,20 @@ public class CredentialProviderUtility {
String
password
=
cmd
.
getOptionValue
(
"p"
);
if
(
userName
!=
null
&&
password
!=
null
)
{
String
encryptedPassword
=
UserDao
.
encrypt
(
password
,
userName
);
String
encryptedPassword
=
UserDao
.
encrypt
(
password
);
boolean
silentOption
=
cmd
.
hasOption
(
"s"
);
if
(
silentOption
)
{
System
.
out
.
println
(
encryptedPassword
);
}
else
{
System
.
out
.
println
(
"Your encrypted password is : "
+
encryptedPassword
);
}
}
else
{
System
.
out
.
println
(
"Please provide username and password as input. Usage:"
+
" cputil.py -g -u <username> -p <password>"
);
System
.
out
.
println
(
"Please provide username and password as input. Usage: cputil.py -g -u <username> -p <password>"
);
}
return
;
}
}
catch
(
Exception
e
)
{
System
.
out
.
println
(
"Exception while generatePassword "
+
e
.
getMessage
());
return
;
...
...
@@ -112,36 +104,42 @@ public class CredentialProviderUtility {
CredentialProvider
provider
=
getCredentialProvider
(
textDevice
);
if
(
provider
!=
null
)
{
char
[]
cred
;
for
(
String
key
:
KEYS
)
{
cred
=
getPassword
(
textDevice
,
key
);
char
[]
cred
=
getPassword
(
textDevice
,
key
);
// create a credential entry and store it
boolean
overwrite
=
true
;
if
(
provider
.
getCredentialEntry
(
key
)
!=
null
)
{
String
choice
=
textDevice
.
readLine
(
"Entry for %s already exists. Overwrite? (y/n) [y]:"
,
key
);
overwrite
=
StringUtils
.
isEmpty
(
choice
)
||
choice
.
equalsIgnoreCase
(
"y"
);
boolean
overwrite
=
StringUtils
.
isEmpty
(
choice
)
||
choice
.
equalsIgnoreCase
(
"y"
);
if
(
overwrite
)
{
provider
.
deleteCredentialEntry
(
key
);
provider
.
flush
();
provider
.
createCredentialEntry
(
key
,
cred
);
provider
.
flush
();
textDevice
.
printf
(
"Entry for %s was overwritten with the new value.\n"
,
key
);
}
else
{
textDevice
.
printf
(
"Entry for %s was not overwritten.\n"
,
key
);
}
}
else
{
provider
.
createCredentialEntry
(
key
,
cred
);
provider
.
flush
();
}
}
}
}
private
static
void
createOptions
(
Options
options
)
{
private
static
Options
createOptions
()
{
Options
options
=
new
Options
();
options
.
addOption
(
"g"
,
"generatePassword"
,
false
,
"Generate Password"
);
options
.
addOption
(
"s"
,
"silent"
,
false
,
"Silent"
);
options
.
addOption
(
"u"
,
"username"
,
true
,
"UserName"
);
options
.
addOption
(
"p"
,
"password"
,
true
,
"Password"
);
return
options
;
}
/**
...
...
@@ -151,32 +149,39 @@ public class CredentialProviderUtility {
* @return the password.
*/
private
static
char
[]
getPassword
(
TextDevice
textDevice
,
String
key
)
{
boolean
noMatch
;
char
[]
cred
=
new
char
[
0
];
char
[]
passwd1
;
char
[]
passwd2
;
do
{
passwd1
=
textDevice
.
readPassword
(
"Please enter the password value for %s:"
,
key
);
passwd2
=
textDevice
.
readPassword
(
"Please enter the password value for %s again:"
,
key
);
noMatch
=
!
Arrays
.
equals
(
passwd1
,
passwd2
);
if
(
noMatch
)
{
if
(
passwd1
!=
null
)
{
Arrays
.
fill
(
passwd1
,
' '
);
}
char
[]
ret
;
while
(
true
)
{
char
[]
passwd1
=
textDevice
.
readPassword
(
"Please enter the password value for %s:"
,
key
);
char
[]
passwd2
=
textDevice
.
readPassword
(
"Please enter the password value for %s again:"
,
key
);
boolean
isMatch
=
!
Arrays
.
equals
(
passwd1
,
passwd2
);
if
(!
isMatch
)
{
textDevice
.
printf
(
"Password entries don't match. Please try again.\n"
);
}
else
{
if
(
passwd1
.
length
==
0
)
{
if
(
passwd1
==
null
||
passwd1
.
length
==
0
)
{
textDevice
.
printf
(
"An empty password is not valid. Please try again.\n"
);
noMatch
=
true
;
}
else
{
cred
=
passwd1
;
ret
=
passwd1
;
if
(
passwd2
!=
null
)
{
Arrays
.
fill
(
passwd2
,
' '
);
}
break
;
}
}
if
(
passwd1
!=
null
)
{
Arrays
.
fill
(
passwd1
,
' '
);
}
if
(
passwd2
!=
null
)
{
Arrays
.
fill
(
passwd2
,
' '
);
}
}
while
(
noMatch
);
return
cred
;
}
return
ret
;
}
/**\
...
...
@@ -190,7 +195,9 @@ public class CredentialProviderUtility {
if
(
providerPath
!=
null
)
{
Configuration
conf
=
new
Configuration
(
false
);
conf
.
set
(
CredentialProviderFactory
.
CREDENTIAL_PROVIDER_PATH
,
providerPath
);
return
CredentialProviderFactory
.
getProviders
(
conf
).
get
(
0
);
}
...
...
webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java
View file @
5783b701
...
...
@@ -39,19 +39,20 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
import
org.springframework.security.core.userdetails.UsernameNotFoundException
;
import
java.security.MessageDigest
;
import
org.springframework.security.core.AuthenticationException
;
import
org.springframework.security.crypto.bcrypt.BCrypt
;
import
org.springframework.util.StringUtils
;
@Repository
public
class
UserDao
{
private
static
final
String
DEFAULT_USER_CREDENTIALS_PROPERTIES
=
"users-credentials.properties"
;
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
UserDao
.
class
);
private
Properties
userLogins
;
private
static
final
String
DEFAULT_USER_CREDENTIALS_PROPERTIES
=
"users-credentials.properties"
;
private
static
final
ShaPasswordEncoder
sha256Encoder
=
new
ShaPasswordEncoder
(
256
);
private
static
boolean
v1ValidationEnabled
=
true
;
private
static
boolean
v2ValidationEnabled
=
true
;
private
Properties
userLogins
=
new
Properties
();
@PostConstruct
public
void
init
()
{
...
...
@@ -59,49 +60,61 @@ public class UserDao {
}
void
loadFileLoginsDetails
()
{
userLogins
.
clear
();
InputStream
inStr
=
null
;
try
{
Configuration
configuration
=
ApplicationProperties
.
get
();
v1ValidationEnabled
=
configuration
.
getBoolean
(
"atlas.authentication.method.file.v1-validation.enabled"
,
true
);
v2ValidationEnabled
=
configuration
.
getBoolean
(
"atlas.authentication.method.file.v2-validation.enabled"
,
true
);
inStr
=
ApplicationProperties
.
getFileAsInputStream
(
configuration
,
"atlas.authentication.method.file.filename"
,
DEFAULT_USER_CREDENTIALS_PROPERTIES
);
userLogins
=
new
Properties
();
userLogins
.
load
(
inStr
);
}
catch
(
IOException
|
AtlasException
e
)
{
LOG
.
error
(
"Error while reading user.properties file"
,
e
);
throw
new
RuntimeException
(
e
);
}
finally
{
if
(
inStr
!=
null
)
{
if
(
inStr
!=
null
)
{
try
{
inStr
.
close
();
}
catch
(
Exception
excp
)
{
}
catch
(
Exception
excp
)
{
// ignore
}
}
}
}
public
User
loadUserByUsername
(
final
String
username
)
throws
AuthenticationException
{
public
User
loadUserByUsername
(
final
String
username
)
throws
AuthenticationException
{
String
userdetailsStr
=
userLogins
.
getProperty
(
username
);
if
(
userdetailsStr
==
null
||
userdetailsStr
.
isEmpty
())
{
throw
new
UsernameNotFoundException
(
"Username not found."
+
username
);
throw
new
UsernameNotFoundException
(
"Username not found."
+
username
);
}
String
password
=
""
;
String
role
=
""
;
String
dataArr
[]
=
userdetailsStr
.
split
(
"::"
);
String
[]
dataArr
=
userdetailsStr
.
split
(
"::"
);
if
(
dataArr
!=
null
&&
dataArr
.
length
==
2
)
{
role
=
dataArr
[
0
];
password
=
dataArr
[
1
];
}
else
{
LOG
.
error
(
"User role credentials is not set properly for {}"
,
username
);
throw
new
AtlasAuthenticationException
(
"User role credentials is not set properly for "
+
username
);
}
List
<
GrantedAuthority
>
grantedAuths
=
new
ArrayList
<>();
if
(
StringUtils
.
hasText
(
role
))
{
grantedAuths
.
add
(
new
SimpleGrantedAuthority
(
role
));
}
else
{
LOG
.
error
(
"User role credentials is not set properly for {}"
,
username
);
throw
new
AtlasAuthenticationException
(
"User role credentials is not set properly for "
+
username
);
}
...
...
@@ -115,7 +128,91 @@ public class UserDao {
this
.
userLogins
=
userLogins
;
}
public
static
String
getSha256Hash
(
String
base
)
throws
AtlasAuthenticationException
{
public
static
String
encrypt
(
String
password
)
{
String
ret
=
null
;
try
{
ret
=
BCrypt
.
hashpw
(
password
,
BCrypt
.
gensalt
());
}
catch
(
Throwable
excp
)
{
LOG
.
warn
(
"UserDao.encrypt(): failed"
,
excp
);
}
return
ret
;
}
public
static
boolean
checkEncrypted
(
String
password
,
String
encryptedPwd
,
String
userName
)
{
boolean
ret
=
checkPasswordBCrypt
(
password
,
encryptedPwd
);
if
(!
ret
&&
v2ValidationEnabled
)
{
ret
=
checkPasswordSHA256WithSalt
(
password
,
encryptedPwd
,
userName
);
}
if
(!
ret
&&
v1ValidationEnabled
)
{
ret
=
checkPasswordSHA256
(
password
,
encryptedPwd
);
}
return
ret
;
}
private
static
boolean
checkPasswordBCrypt
(
String
password
,
String
encryptedPwd
)
{
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"checkPasswordBCrypt()"
);
}
boolean
ret
=
false
;
try
{
ret
=
BCrypt
.
checkpw
(
password
,
encryptedPwd
);
}
catch
(
Throwable
excp
)
{
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"checkPasswordBCrypt(): failed"
,
excp
);
}
}
return
ret
;
}
private
static
boolean
checkPasswordSHA256WithSalt
(
String
password
,
String
encryptedPwd
,
String
salt
)
{
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"checkPasswordSHA256WithSalt()"
);
}
boolean
ret
=
false
;
try
{
String
hash
=
sha256Encoder
.
encodePassword
(
password
,
salt
);
ret
=
hash
!=
null
&&
hash
.
equals
(
encryptedPwd
);
}
catch
(
Throwable
excp
)
{
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"checkPasswordSHA256WithSalt(): failed"
,
excp
);
}
}
return
ret
;
}
private
static
boolean
checkPasswordSHA256
(
String
password
,
String
encryptedPwd
)
{
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"checkPasswordSHA256()"
);
}
boolean
ret
=
false
;
try
{
String
hash
=
getSha256Hash
(
password
);
ret
=
hash
!=
null
&&
hash
.
equals
(
encryptedPwd
);
}
catch
(
Throwable
excp
)
{
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"checkPasswordSHA256(): failed"
,
excp
);
}
}
return
ret
;
}
private
static
String
getSha256Hash
(
String
base
)
throws
AtlasAuthenticationException
{
try
{
MessageDigest
digest
=
MessageDigest
.
getInstance
(
"SHA-256"
);
byte
[]
hash
=
digest
.
digest
(
base
.
getBytes
(
"UTF-8"
));
...
...
@@ -123,17 +220,17 @@ public class UserDao {
for
(
byte
aHash
:
hash
)
{
String
hex
=
Integer
.
toHexString
(
0xff
&
aHash
);
if
(
hex
.
length
()
==
1
)
hexString
.
append
(
'0'
);
if
(
hex
.
length
()
==
1
)
{
hexString
.
append
(
'0'
);
}
hexString
.
append
(
hex
);
}
return
hexString
.
toString
();
return
hexString
.
toString
();
}
catch
(
Exception
ex
)
{
throw
new
AtlasAuthenticationException
(
"Exception while encoding password."
,
ex
);
}
}
public
static
String
encrypt
(
String
password
,
String
salt
)
{
return
sha256Encoder
.
encodePassword
(
password
,
salt
);
}
}
webapp/src/main/java/org/apache/atlas/web/security/AtlasFileAuthenticationProvider.java
View file @
5783b701
...
...
@@ -16,9 +16,7 @@
*/
package
org
.
apache
.
atlas
.
web
.
security
;
import
org.apache.atlas.ApplicationProperties
;
import
org.apache.atlas.web.dao.UserDao
;
import
org.apache.commons.configuration.Configuration
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.security.authentication.BadCredentialsException
;
...
...
@@ -30,7 +28,6 @@ import org.springframework.security.core.userdetails.UserDetails;
import
org.springframework.security.core.userdetails.UserDetailsService
;
import
org.springframework.stereotype.Component
;
import
javax.annotation.PostConstruct
;
import
javax.inject.Inject
;
import
java.util.Collection
;
...
...
@@ -41,57 +38,42 @@ public class AtlasFileAuthenticationProvider extends AtlasAbstractAuthentication
private
static
Logger
logger
=
LoggerFactory
.
getLogger
(
AtlasFileAuthenticationProvider
.
class
);
private
final
UserDetailsService
userDetailsService
;
private
boolean
v1ValidationEnabled
=
true
;
@Inject
public
AtlasFileAuthenticationProvider
(
UserDetailsService
userDetailsService
)
{
this
.
userDetailsService
=
userDetailsService
;
}
@PostConstruct
public
void
setup
()
{
try
{
Configuration
configuration
=
ApplicationProperties
.
get
();
v1ValidationEnabled
=
configuration
.
getBoolean
(
"atlas.authentication.method.file.v1-validation.enabled"
,
true
);
}
catch
(
Exception
e
)
{
logger
.
error
(
"Exception while setup"
,
e
);
}
}
@Override
public
Authentication
authenticate
(
Authentication
authentication
)
throws
AuthenticationException
{
String
username
=
authentication
.
getName
();
String
password
=
(
String
)
authentication
.
getCredentials
();
if
(
username
==
null
||
username
.
isEmpty
())
{
logger
.
error
(
"Username can't be null or empty."
);
throw
new
BadCredentialsException
(
"Username can't be null or empty."
);
throw
new
BadCredentialsException
(
"Username can't be null or empty."
);
}
if
(
password
==
null
||
password
.
isEmpty
())
{
logger
.
error
(
"Password can't be null or empty."
);
throw
new
BadCredentialsException
(
"Password can't be null or empty."
);
throw
new
BadCredentialsException
(
"Password can't be null or empty."
);
}
UserDetails
user
=
userDetailsService
.
loadUserByUsername
(
username
);
String
encodedPassword
=
UserDao
.
encrypt
(
password
,
username
);
boolean
isValidPassword
=
encodedPassword
.
equals
(
user
.
getPassword
());
boolean
isValidPassword
=
UserDao
.
checkEncrypted
(
password
,
user
.
getPassword
(),
username
);
if
(!
isValidPassword
&&
v1ValidationEnabled
)
{
encodedPassword
=
UserDao
.
getSha256Hash
(
password
);
}
if
(!
encodedPassword
.
equals
(
user
.
getPassword
()))
{
if
(!
isValidPassword
)
{
logger
.
error
(
"Wrong password "
+
username
);
throw
new
BadCredentialsException
(
"Wrong password"
);
}
Collection
<?
extends
GrantedAuthority
>
authorities
=
user
.
getAuthorities
();
authentication
=
new
UsernamePasswordAuthenticationToken
(
username
,
password
,
authorities
);
return
authentication
;
}
}
webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java
View file @
5783b701
...
...
@@ -92,6 +92,7 @@ public class FileAuthenticationTest {
private
void
setupUserCredential
(
String
tmpDir
)
throws
Exception
{
StringBuilder
credentialFileStr
=
new
StringBuilder
(
1024
);
credentialFileStr
.
append
(
"adminv3=ADMIN::$2a$10$ZVnkc2if06JMLCJEAhTKbOPeWDXTCFLL8zMA6FzZoP.bu8ThT43ha\n"
);
credentialFileStr
.
append
(
"admin=ADMIN::a4a88c0872bf652bb9ed803ece5fd6e82354838a9bf59ab4babb1dab322154e1\n"
);
credentialFileStr
.
append
(
"adminv1=ADMIN::8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918\n"
);
credentialFileStr
.
append
(
"michael=DATA_SCIENTIST::95bfb24de17d285d734b9eaa9109bfe922adc85f20d2e5e66a78bddb4a4ebddb\n"
);
...
...
@@ -112,6 +113,18 @@ public class FileAuthenticationTest {
@Test
public
void
testValidUserLoginWithV3password
()
{
when
(
authentication
.
getName
()).
thenReturn
(
"adminv3"
);
when
(
authentication
.
getCredentials
()).
thenReturn
(
"admin"
);
Authentication
auth
=
authProvider
.
authenticate
(
authentication
);
LOG
.
debug
(
" {}"
,
auth
);
assertTrue
(
auth
.
isAuthenticated
());
}
@Test
public
void
testValidUserLogin
()
{
when
(
authentication
.
getName
()).
thenReturn
(
"admin"
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment