Gitlab Community Edition Instance
Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
cdstar
cdstar
Commits
948d2983
Commit
948d2983
authored
Oct 18, 2019
by
mhellka
Browse files
Added FileRealm (incomplete)
parent
4b7a9983
Pipeline
#111690
failed with stage
in 2 minutes and 11 seconds
Changes
2
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
cdstar-auth/pom.xml
View file @
948d2983
...
...
@@ -29,5 +29,13 @@
<artifactId>
cdstar-config
</artifactId>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
com.fasterxml.jackson.dataformat
</groupId>
<artifactId>
jackson-dataformat-yaml
</artifactId>
</dependency>
<dependency>
<groupId>
com.fasterxml.jackson.core
</groupId>
<artifactId>
jackson-databind
</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
cdstar-auth/src/main/java/de/gwdg/cdstar/auth/realm/FileRealm.java
0 → 100644
View file @
948d2983
package
de.gwdg.cdstar.auth.realm
;
import
java.io.Console
;
import
java.io.IOException
;
import
java.nio.file.FileVisitResult
;
import
java.nio.file.FileVisitor
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
java.nio.file.SimpleFileVisitor
;
import
java.nio.file.attribute.BasicFileAttributes
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Optional
;
import
java.util.Map.Entry
;
import
java.util.Set
;
import
java.util.concurrent.ConcurrentHashMap
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.dataformat.yaml.YAMLMapper
;
import
de.gwdg.cdstar.Utils
;
import
de.gwdg.cdstar.auth.Credentials
;
import
de.gwdg.cdstar.auth.KnownPrincipalCredentials
;
import
de.gwdg.cdstar.auth.Permission
;
import
de.gwdg.cdstar.auth.Principal
;
import
de.gwdg.cdstar.auth.Session
;
import
de.gwdg.cdstar.auth.StringPermission
;
import
de.gwdg.cdstar.auth.UsernamePasswordCredentials
;
import
de.gwdg.cdstar.auth.simple.Account
;
import
de.gwdg.cdstar.auth.simple.QName
;
import
de.gwdg.cdstar.auth.simple.SimpleAuthorizer
;
import
de.gwdg.cdstar.runtime.Config
;
import
de.gwdg.cdstar.runtime.ConfigException
;
import
de.gwdg.cdstar.runtime.Plugin
;
/**
* A {@link SimpleAuthorizer} subclass that can read and store its state from
* yaml files.
*/
@Plugin
public
class
FileRealm
implements
Authorizer
,
Authenticator
,
GroupResolver
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
FileRealm
.
class
);
private
static
final
ObjectMapper
yaml
=
new
YAMLMapper
();
private
final
class
ConfigDirVisitor
extends
SimpleFileVisitor
<
Path
>
{
@Override
public
FileVisitResult
preVisitDirectory
(
Path
dir
,
BasicFileAttributes
attrs
)
throws
IOException
{
if
(
Files
.
isHidden
(
dir
))
return
FileVisitResult
.
SKIP_SUBTREE
;
return
super
.
preVisitDirectory
(
dir
,
attrs
);
}
@Override
public
FileVisitResult
visitFile
(
Path
file
,
BasicFileAttributes
attrs
)
throws
IOException
{
if
(
Files
.
isHidden
(
file
))
return
FileVisitResult
.
CONTINUE
;
if
(!(
file
.
getFileName
().
toString
().
endsWith
(
".yaml"
)
||
file
.
getFileName
().
toString
().
endsWith
(
".yml"
)))
return
FileVisitResult
.
CONTINUE
;
load
(
file
);
return
FileVisitResult
.
CONTINUE
;
}
}
private
final
class
ConfigSource
{
Path
file
;
FileConfig
config
;
String
domain
;
Map
<
QName
,
FileAccount
>
accounts
;
Map
<
QName
,
Set
<
QName
>>
groupRoles
;
Map
<
QName
,
Set
<
StringPermission
>>
roles
;
ConfigSource
(
Path
src
,
FileConfig
cfg
)
{
String
srcString
=
file
.
toAbsolutePath
().
toString
();
domain
=
Utils
.
notNullOrEmpty
(
cfg
.
domain
)
?
cfg
.
domain
:
defaultDomain
;
accounts
=
new
HashMap
<>(
cfg
.
users
.
size
());
groupRoles
=
new
HashMap
<>(
cfg
.
groups
.
size
());
roles
=
new
HashMap
<>(
cfg
.
roles
.
size
());
cfg
.
users
.
forEach
((
name
,
user
)
->
{
FileAccount
acc
=
getAccount
(
QName
.
fromString
(
name
,
domain
));
if
(
Utils
.
notNullOrEmpty
(
user
.
password
))
{
int
index
;
if
((
index
=
user
.
password
.
indexOf
(
":"
))
==
-
1
)
throw
new
IllegalArgumentException
(
"Invalid pasword hash for user: "
+
name
);
final
byte
[]
key
=
Utils
.
base64decode
(
user
.
password
.
substring
(
0
,
index
));
final
byte
[]
hash
=
Utils
.
base64decode
(
user
.
password
.
substring
(
index
+
1
));
acc
.
withPassword
(
hash
,
key
);
}
if
(
Utils
.
notNullOrEmpty
(
user
.
roles
))
user
.
roles
.
forEach
(
roleName
->
acc
.
withRole
(
QName
.
fromString
(
roleName
,
acc
.
getDomain
())));
if
(
Utils
.
notNullOrEmpty
(
user
.
permissions
))
user
.
permissions
.
stream
().
map
(
StringPermission:
:
parse
).
forEach
(
acc:
:
withPermission
);
});
cfg
.
groups
.
forEach
((
name
,
grp
)
->
{
QName
groupName
=
QName
.
fromString
(
name
,
domain
);
if
(
Utils
.
notNullOrEmpty
(
grp
.
roles
))
{
Set
<
QName
>
roleSet
=
groupRoles
.
computeIfAbsent
(
groupName
,
gn
->
new
HashSet
<>());
grp
.
roles
.
forEach
(
r
->
{
QName
roleName
=
QName
.
fromString
(
r
,
groupName
.
getDomain
());
getRole
(
roleName
);
roleSet
.
add
(
roleName
);
});
}
if
(
Utils
.
notNullOrEmpty
(
grp
.
members
))
{
grp
.
members
.
forEach
(
member
->
{
getAccount
(
QName
.
fromString
(
member
,
groupName
.
getDomain
())).
withGroup
(
groupName
);
});
}
});
cfg
.
roles
.
forEach
((
name
,
role
)
->
{
Set
<
StringPermission
>
roleSet
=
getRole
(
QName
.
fromString
(
name
,
domain
));
role
.
permissions
.
stream
().
map
(
StringPermission:
:
parse
).
forEach
(
roleSet:
:
add
);
});
}
private
FileAccount
getAccount
(
QName
name
)
{
return
accounts
.
computeIfAbsent
(
name
,
qname
->
new
FileAccount
(
this
,
qname
));
}
private
Set
<
StringPermission
>
getRole
(
QName
name
)
{
return
roles
.
computeIfAbsent
(
name
,
r
->
new
HashSet
<>());
}
}
final
static
class
FileConfig
{
String
domain
;
Map
<
String
,
FileUser
>
users
;
Map
<
String
,
FileRole
>
roles
;
Map
<
String
,
FileGroup
>
groups
;
final
static
class
FileUser
{
String
password
;
Set
<
String
>
permissions
;
Set
<
String
>
roles
;
// Map<String, String> properties;
}
final
static
class
FileGroup
{
Set
<
String
>
members
;
Set
<
String
>
roles
;
}
final
static
class
FileRole
{
// String description;
Set
<
String
>
permissions
;
}
}
private
String
name
;
private
String
defaultDomain
;
private
List
<
ConfigSource
>
sources
=
new
ArrayList
<>();
private
Map
<
QName
,
Set
<
QName
>>
groups
;
public
FileRealm
(
Config
config
)
throws
ConfigException
,
IOException
{
name
=
config
.
get
(
"_name"
,
"file"
);
defaultDomain
=
config
.
get
(
"domain"
,
name
);
load
(
Paths
.
get
(
config
.
get
(
"source"
)));
}
public
FileRealm
(
String
name
,
String
domain
)
throws
ConfigException
,
IOException
{
this
.
name
=
name
;
this
.
defaultDomain
=
domain
;
}
private
void
load
(
Path
source
)
throws
IOException
{
if
(
Files
.
isDirectory
(
source
))
{
try
{
Files
.
walkFileTree
(
source
,
new
ConfigDirVisitor
());
}
catch
(
IOException
e
)
{
throw
new
IOException
(
"Unable to scan config directory: "
+
source
,
e
);
}
return
;
}
try
{
ConfigSource
src
=
new
ConfigSource
(
source
,
yaml
.
readValue
(
source
.
toFile
(),
FileConfig
.
class
));
// register/replace and flush caches
}
catch
(
IOException
e
)
{
throw
new
IOException
(
"Failed to load config file: "
+
source
,
e
);
}
}
@Override
public
String
getName
()
{
return
name
;
}
@Override
public
boolean
isMemberOf
(
Session
session
,
String
groupName
,
String
groupDomain
)
{
final
FileAccount
acc
=
resolveAccount
(
session
);
if
(
acc
==
null
)
return
false
;
QName
qgroup
=
new
QName
(
groupName
,
groupDomain
!=
null
?
groupDomain
:
defaultDomain
);
return
groups
.
getOrDefault
(
qgroup
,
Collections
.
emptySet
()).
contains
(
acc
.
qname
)
||
acc
.
isMemberOf
(
new
QName
(
groupName
,
groupDomain
));
}
private
FileAccount
resolveAccount
(
Session
session
)
{
if
(
session
==
null
)
return
null
;
if
(
session
instanceof
FileSession
)
{
final
FileSession
fs
=
(
FileSession
)
session
;
return
fs
.
account
.
realm
==
this
?
fs
.
account
:
null
;
}
return
resolveAccount
(
session
.
getPrincipal
().
getId
(),
session
.
getPrincipal
().
getDomain
());
}
private
FileAccount
resolveAccount
(
String
name
,
String
domain
)
{
return
accounts
.
get
(
new
QName
(
name
,
domain
!=
null
?
domain
:
defaultDomain
));
}
@Override
public
Session
login
(
Credentials
request
)
{
if
(
request
instanceof
UsernamePasswordCredentials
)
{
final
UsernamePasswordCredentials
creds
=
(
UsernamePasswordCredentials
)
request
;
final
FileAccount
account
=
resolveAccount
(
creds
.
getName
(),
creds
.
getDomain
());
if
(
account
!=
null
&&
account
.
checkPassword
(
creds
.
getPassword
()))
return
new
FileSession
(
account
,
false
);
}
if
(
request
instanceof
KnownPrincipalCredentials
)
{
final
Principal
principal
=
((
KnownPrincipalCredentials
)
request
).
getPrincipal
();
if
(
principal
instanceof
FileAccount
&&
((
FileAccount
)
principal
).
realm
==
this
)
return
new
FileSession
((
FileAccount
)
principal
,
true
);
}
return
null
;
}
@Override
public
void
logout
(
Session
who
)
{
}
@Override
public
boolean
isPermitted
(
Session
session
,
Permission
p
)
{
// TODO Auto-generated method stub
return
false
;
}
private
class
FileAccount
implements
Principal
{
QName
name
;
byte
[]
passwordHash
;
byte
[]
signKey
;
final
Set
<
QName
>
groups
=
new
HashSet
<>();
final
Set
<
QName
>
roles
=
new
HashSet
<>();
final
Set
<
StringPermission
>
permissions
=
new
HashSet
<>();
final
ConfigSource
source
;
public
FileAccount
(
ConfigSource
src
,
QName
name
)
{
this
.
source
=
src
;
this
.
name
=
name
;
}
public
FileAccount
withGroup
(
QName
group
)
{
groups
.
add
(
group
);
return
this
;
}
public
FileAccount
withRole
(
QName
role
)
{
roles
.
add
(
role
);
return
this
;
}
public
FileAccount
withPassword
(
byte
[]
hash
,
byte
[]
key
)
{
passwordHash
=
Arrays
.
copyOf
(
hash
,
hash
.
length
);
signKey
=
Arrays
.
copyOf
(
key
,
key
.
length
);
return
this
;
}
public
FileAccount
withPermission
(
StringPermission
p
)
{
permissions
.
add
(
p
);
return
this
;
}
public
boolean
checkPassword
(
char
[]
password
)
{
if
(
passwordHash
!=
null
)
return
Utils
.
signCheck
(
passwordHash
,
Utils
.
toBytes
(
password
),
signKey
);
return
false
;
}
public
boolean
isMemberOf
(
QName
qName
)
{
return
groups
.
contains
(
qName
);
}
@Override
public
String
getId
()
{
return
name
.
getName
();
}
@Override
public
String
getDomain
()
{
return
name
.
getDomain
();
}
}
private
static
class
FileSession
implements
Session
{
private
FileAccount
account
;
private
boolean
remembered
;
public
FileSession
(
FileAccount
account
,
boolean
remembered
)
{
this
.
account
=
account
;
this
.
remembered
=
remembered
;
}
@Override
public
boolean
isRemembered
()
{
return
remembered
;
}
@Override
public
Authenticator
getAuthenticator
()
{
return
account
.
realm
;
}
@Override
public
Principal
getPrincipal
()
{
return
account
;
}
}
}
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