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
5 years ago
by
Madhan Neethiraj
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ATLAS-3666: updated file-based authentication to use BCrypt
parent
f1df13dc
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
194 additions
and
95 deletions
+194
-95
CredentialProviderUtility.java
...java/org/apache/atlas/util/CredentialProviderUtility.java
+47
-40
UserDao.java
webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java
+123
-26
AtlasFileAuthenticationProvider.java
...e/atlas/web/security/AtlasFileAuthenticationProvider.java
+11
-29
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,34 +72,29 @@ 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
);
boolean
generatePasswordOption
=
cmd
.
hasOption
(
"g"
);
CommandLine
cmd
=
new
DefaultParser
().
parse
(
createOptions
(),
args
);
boolean
generatePasswordOption
=
cmd
.
hasOption
(
"g"
);
if
(
generatePasswordOption
)
{
String
userName
=
cmd
.
getOptionValue
(
"u"
);
String
password
=
cmd
.
getOptionValue
(
"p"
);
if
(
userName
!=
null
&&
password
!=
null
)
{
String
encryptedPassword
=
UserDao
.
encrypt
(
password
,
userName
);
boolean
silentOption
=
cmd
.
hasOption
(
"s"
);
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"
);
String
choice
=
textDevice
.
readLine
(
"Entry for %s already exists. Overwrite? (y/n) [y]:"
,
key
);
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
);
}
...
...
This diff is collapsed.
Click to expand it.
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
static
final
ShaPasswordEncoder
sha256Encoder
=
new
ShaPasswordEncoder
(
256
);
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
password
=
""
;
String
role
=
""
;
String
[]
dataArr
=
userdetailsStr
.
split
(
"::"
);
if
(
dataArr
!=
null
&&
dataArr
.
length
==
2
)
{
role
=
dataArr
[
0
];
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,25 +128,109 @@ 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
{
MessageDigest
digest
=
MessageDigest
.
getInstance
(
"SHA-256"
);
byte
[]
hash
=
digest
.
digest
(
base
.
getBytes
(
"UTF-8"
));
StringBuffer
hexString
=
new
StringBuffer
();
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"
));
StringBuffer
hexString
=
new
StringBuffer
();
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
);
}
}
This diff is collapsed.
Click to expand it.
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."
);
}
UserDetails
user
=
userDetailsService
.
loadUserByUsername
(
username
);
String
encodedPassword
=
UserDao
.
encrypt
(
password
,
username
);
boolean
isValidPassword
=
encodedPassword
.
equals
(
user
.
getPassword
());
if
(!
isValidPassword
&&
v1ValidationEnabled
)
{
encodedPassword
=
UserDao
.
getSha256Hash
(
password
);
throw
new
BadCredentialsException
(
"Password can't be null or empty."
);
}
if
(!
encodedPassword
.
equals
(
user
.
getPassword
()))
{
UserDetails
user
=
userDetailsService
.
loadUserByUsername
(
username
);
boolean
isValidPassword
=
UserDao
.
checkEncrypted
(
password
,
user
.
getPassword
(),
username
);
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
;
}
}
This diff is collapsed.
Click to expand it.
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"
);
...
...
This diff is collapsed.
Click to expand it.
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