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
dad
vitamui-pr-pastis
Commits
cc883b9d
Unverified
Commit
cc883b9d
authored
Oct 30, 2020
by
Le Roy Loïc
Committed by
GitHub
Oct 30, 2020
Browse files
Merge pull request #50 from ProgrammeVitam/security_fixes
Correction de bugs en lien avec la sécurité et CAS
parents
aca573fd
814d3258
Changes
16
Show whitespace changes
Inline
Side-by-side
api/api-iam/iam-external-client/src/test/java/fr/gouv/vitamui/iam/external/client/CasExternalRestClientTest.java
View file @
cc883b9d
...
...
@@ -38,7 +38,7 @@ public class CasExternalRestClientTest extends AbstractServerIdentityBuilder {
.
thenReturn
(
new
ResponseEntity
<>(
HttpStatus
.
OK
));
final
String
superUser
=
"julien@vitamui.com"
;
final
String
authToken
=
"TOK
yyy
"
;
final
String
authToken
=
"TOK
-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu
"
;
client
.
logout
(
header
,
authToken
,
superUser
);
final
String
path
=
RestApi
.
CAS_LOGOUT_PATH
+
"?authToken="
+
authToken
+
"&superUser="
+
superUser
;
assertThat
(
argumentCaptor
.
getValue
().
toString
()).
endsWith
(
path
.
replaceAll
(
CommonConstants
.
EMAIL_SEPARATOR
,
"%40"
));
...
...
api/api-iam/iam-internal-client/src/test/java/fr/gouv/vitamui/iam/internal/client/CasInternalRestClientTest.java
View file @
cc883b9d
...
...
@@ -39,7 +39,7 @@ public class CasInternalRestClientTest extends AbstractServerIdentityBuilder {
.
thenReturn
(
new
ResponseEntity
<>(
HttpStatus
.
OK
));
final
String
superUser
=
"julien@vitamui.com"
;
final
String
authToken
=
"TOK
xxx
"
;
final
String
authToken
=
"TOK
-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu
"
;
client
.
logout
(
header
,
authToken
,
superUser
);
final
String
path
=
RestApi
.
CAS_LOGOUT_PATH
+
"?authToken="
+
authToken
+
"&superUser="
+
superUser
;
assertThat
(
argumentCaptor
.
getValue
().
toString
()).
endsWith
(
path
.
replaceAll
(
CommonConstants
.
EMAIL_SEPARATOR
,
"%40"
));
...
...
api/api-iam/iam-internal/pom.xml
View file @
cc883b9d
...
...
@@ -168,6 +168,52 @@
<artifactId>
common-private
</artifactId>
</dependency>
<!-- Apereo CAS Server Core Api Ticket -->
<dependency>
<groupId>
org.apereo.cas
</groupId>
<artifactId>
cas-server-core-api-ticket
</artifactId>
<exclusions>
<exclusion>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-context
</artifactId>
</exclusion>
<exclusion>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-commons
</artifactId>
</exclusion>
<exclusion>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-actuator
</artifactId>
</exclusion>
<exclusion>
<groupId>
com.zaxxer
</groupId>
<artifactId>
*
</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>
org.apereo.cas
</groupId>
<artifactId>
cas-server-core-tickets-api
</artifactId>
<exclusions>
<exclusion>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-context
</artifactId>
</exclusion>
<exclusion>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-commons
</artifactId>
</exclusion>
<exclusion>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-actuator
</artifactId>
</exclusion>
<exclusion>
<groupId>
com.zaxxer
</groupId>
<artifactId>
*
</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Documentation -->
<dependency>
<groupId>
io.springfox
</groupId>
...
...
api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java
View file @
cc883b9d
...
...
@@ -49,6 +49,8 @@ import javax.validation.constraints.NotNull;
import
org.apache.commons.lang.StringUtils
;
import
org.apache.commons.lang.time.DateUtils
;
import
org.apereo.cas.util.DefaultUniqueTicketIdGenerator
;
import
org.apereo.cas.ticket.UniqueTicketIdGenerator
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.data.mongodb.core.MongoTemplate
;
...
...
@@ -162,6 +164,8 @@ public class CasInternalService {
@SuppressWarnings
(
"unused"
)
private
static
final
VitamUILogger
LOGGER
=
VitamUILoggerFactory
.
getInstance
(
CasInternalService
.
class
);
private
static
final
UniqueTicketIdGenerator
TICKET_GENERATOR
=
new
DefaultUniqueTicketIdGenerator
();
@Transactional
public
void
updatePassword
(
final
String
email
,
final
String
rawPassword
)
{
final
User
user
=
checkUserInformations
(
email
);
...
...
@@ -272,7 +276,7 @@ public class CasInternalService {
private
void
createEventsSubrogation
(
final
UserDto
surrogate
,
final
boolean
isSubrogation
)
{
if
(
isSubrogation
)
{
final
Subrogation
subro
=
subrogationRepository
.
findOneBySurrogate
(
surrogate
.
getEmail
());
EventType
type
;
final
EventType
type
;
if
(
surrogate
.
getType
().
equals
(
UserTypeEnum
.
GENERIC
))
{
type
=
EventType
.
EXT_VITAMUI_START_SURROGATE_GENERIC
;
}
...
...
@@ -310,7 +314,7 @@ public class CasInternalService {
}
final
Date
nowPlusXMinutes
=
DateUtils
.
addMinutes
(
new
Date
(),
ttlInMinutes
);
token
.
setUpdatedDate
(
nowPlusXMinutes
);
token
.
setId
(
T
OKEN_PREFIX
+
tokenRepository
.
generateSuperId
().
toUpperCase
(
));
token
.
setId
(
T
ICKET_GENERATOR
.
getNewTicketId
(
TOKEN_PREFIX
));
token
.
setSurrogation
(
isSubrogation
);
tokenRepository
.
save
(
token
);
user
.
setLastConnection
(
OffsetDateTime
.
now
());
...
...
api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceIntegTest.java
View file @
cc883b9d
...
...
@@ -85,7 +85,7 @@ import fr.gouv.vitamui.iam.internal.server.utils.IamServerUtilsTest;
TokenRepository
.
class
},
repositoryBaseClass
=
VitamUIRepositoryImpl
.
class
)
public
final
class
UserInternalServiceIntegTest
extends
AbstractLogbookIntegrationTest
{
private
static
final
String
TOKEN_VALUE
=
"TOK
1234567890
"
;
private
static
final
String
TOKEN_VALUE
=
"TOK
-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu
"
;
private
static
final
String
USER_ID
=
"userId"
;
...
...
api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceTest.java
View file @
cc883b9d
...
...
@@ -67,7 +67,7 @@ import fr.gouv.vitamui.iam.security.service.InternalSecurityService;
*/
public
final
class
UserInternalServiceTest
{
private
static
final
String
TOKEN_VALUE
=
"TOK
1234567890
"
;
private
static
final
String
TOKEN_VALUE
=
"TOK
-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu
"
;
private
static
final
String
USER_ID
=
"userId"
;
...
...
cas/cas-server/README.md
View file @
cc883b9d
...
...
@@ -175,8 +175,8 @@ The call must be a POST request:
The result contains the auth token in a plain response:
`access_token=TOK
5CE669E223C9741AA6DCD46xxx02C8F6FB9DC98DC25B779
&expires_in=28800`
`access_token=TOK
-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu
&expires_in=28800`
or in a JSON response:
`{"access_token":"TOK
5CE669E223C9741AA6DCD46xxx02C8F6FB9DC98DC25B779
","token_type":"bearer","expires_in":28800}`
`{"access_token":"TOK
-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu
","token_type":"bearer","expires_in":28800}`
cas/cas-server/pom.xml
View file @
cc883b9d
...
...
@@ -111,6 +111,11 @@
<artifactId>
cas-server-support-pac4j-core
</artifactId>
<version>
${cas.version}
</version>
</dependency>
<dependency>
<groupId>
org.apereo.cas
</groupId>
<artifactId>
cas-server-support-pac4j-api
</artifactId>
<version>
${cas.version}
</version>
</dependency>
<dependency>
<groupId>
org.apereo.cas
</groupId>
<artifactId>
cas-server-support-pac4j-core-clients
</artifactId>
...
...
@@ -131,6 +136,11 @@
<artifactId>
pac4j-core
</artifactId>
<version>
${pac4j.version}
</version>
</dependency>
<dependency>
<groupId>
org.pac4j
</groupId>
<artifactId>
spring-webmvc-pac4j
</artifactId>
<version>
${pac4j.version}
</version>
</dependency>
<dependency>
<groupId>
org.pac4j
</groupId>
<artifactId>
pac4j-saml-opensamlv3
</artifactId>
...
...
cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/AppConfig.java
View file @
cc883b9d
...
...
@@ -39,9 +39,17 @@ package fr.gouv.vitamui.cas.config;
import
fr.gouv.vitamui.cas.authentication.*
;
import
fr.gouv.vitamui.cas.pm.IamPasswordManagementService
;
import
lombok.SneakyThrows
;
import
lombok.val
;
import
java.util.Collection
;
import
java.util.Objects
;
import
java.util.stream.Collectors
;
import
org.apereo.cas.CentralAuthenticationService
;
import
org.apereo.cas.audit.AuditableExecution
;
import
org.apereo.cas.authentication.*
;
import
org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer
;
import
org.apereo.cas.authentication.AuthenticationHandler
;
import
org.apereo.cas.authentication.AuthenticationMetaDataPopulator
;
import
org.apereo.cas.authentication.AuthenticationPostProcessor
;
import
org.apereo.cas.authentication.principal.PrincipalFactory
;
import
org.apereo.cas.authentication.principal.PrincipalResolver
;
import
org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService
;
...
...
@@ -49,13 +57,27 @@ import org.apereo.cas.configuration.CasConfigurationProperties;
import
org.apereo.cas.pm.PasswordHistoryService
;
import
org.apereo.cas.pm.PasswordManagementService
;
import
org.apereo.cas.services.ServicesManager
;
import
org.apereo.cas.ticket.*
;
import
org.apereo.cas.support.oauth.authenticator.Authenticators
;
import
org.apereo.cas.support.oauth.web.OAuth20HandlerInterceptorAdapter
;
import
org.apereo.cas.support.oauth.web.response.accesstoken.ext.AccessTokenGrantRequestExtractor
;
import
org.apereo.cas.ticket.BaseTicketCatalogConfigurer
;
import
org.apereo.cas.ticket.ExpirationPolicyBuilder
;
import
org.apereo.cas.ticket.TicketCatalog
;
import
org.apereo.cas.ticket.TicketCatalogConfigurer
;
import
org.apereo.cas.ticket.TicketDefinition
;
import
org.apereo.cas.ticket.TicketGrantingTicketFactory
;
import
org.apereo.cas.ticket.UniqueTicketIdGenerator
;
import
org.apereo.cas.ticket.accesstoken.OAuth20AccessTokenFactory
;
import
org.apereo.cas.ticket.accesstoken.OAuth20DefaultAccessToken
;
import
org.apereo.cas.ticket.registry.TicketRegistry
;
import
org.apereo.cas.token.JwtBuilder
;
import
org.apereo.cas.util.crypto.CipherExecutor
;
import
org.pac4j.core.client.Client
;
import
org.pac4j.core.client.DirectClient
;
import
org.pac4j.core.config.Config
;
import
org.pac4j.core.context.session.SessionStore
;
import
org.pac4j.core.http.adapter.JEEHttpActionAdapter
;
import
org.pac4j.springframework.web.SecurityInterceptor
;
import
org.springframework.beans.factory.ObjectProvider
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Qualifier
;
...
...
@@ -83,6 +105,7 @@ import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
import
fr.gouv.vitamui.iam.external.client.IamExternalRestClientFactory
;
import
fr.gouv.vitamui.iam.external.client.IdentityProviderExternalRestClient
;
import
org.springframework.mail.javamail.JavaMailSender
;
import
org.springframework.web.servlet.HandlerInterceptor
;
/**
* Configure all beans to customize the CAS server.
...
...
@@ -196,6 +219,38 @@ public class AppConfig extends BaseTicketCatalogConfigurer {
@Value
(
"${vitamui.cas.identity}"
)
private
String
casIdentity
;
@Autowired
@Qualifier
(
"oauthSecConfig"
)
private
ObjectProvider
<
Config
>
oauthSecConfig
;
@Autowired
@Qualifier
(
"accessTokenGrantRequestExtractors"
)
private
Collection
<
AccessTokenGrantRequestExtractor
>
accessTokenGrantRequestExtractors
;
@Bean
public
SecurityInterceptor
requiresAuthenticationAuthorizeInterceptor
()
{
val
interceptor
=
new
SecurityInterceptor
(
oauthSecConfig
.
getObject
(),
Authenticators
.
CAS_OAUTH_CLIENT
,
JEEHttpActionAdapter
.
INSTANCE
);
interceptor
.
setAuthorizers
(
"none"
);
return
interceptor
;
}
@Bean
public
SecurityInterceptor
requiresAuthenticationAccessTokenInterceptor
()
{
val
secConfig
=
oauthSecConfig
.
getObject
();
val
clients
=
Objects
.
requireNonNull
(
secConfig
).
getClients
().
findAllClients
().
stream
().
filter
(
client
->
client
instanceof
DirectClient
).
map
(
Client:
:
getName
)
.
collect
(
Collectors
.
joining
(
","
));
val
interceptor
=
new
SecurityInterceptor
(
oauthSecConfig
.
getObject
(),
clients
,
JEEHttpActionAdapter
.
INSTANCE
);
interceptor
.
setAuthorizers
(
"none"
);
return
interceptor
;
}
@Bean
public
HandlerInterceptor
oauthHandlerInterceptorAdapter
()
{
return
new
OAuth20HandlerInterceptorAdapter
(
requiresAuthenticationAccessTokenInterceptor
(),
requiresAuthenticationAuthorizeInterceptor
(),
accessTokenGrantRequestExtractors
);
}
@Bean
public
UserAuthenticationHandler
userAuthenticationHandler
()
{
return
new
UserAuthenticationHandler
(
servicesManager
,
principalFactory
,
casRestClient
(),
utils
(),
ipHeaderName
);
...
...
cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/configurer/CustomLoginWebflowConfigurer.java
View file @
cc883b9d
...
...
@@ -134,7 +134,7 @@ public class CustomLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer
createTransitionForState
(
handler
,
AccountDisabledException
.
class
.
getSimpleName
(),
CasWebflowConstants
.
VIEW_ID_ACCOUNT_DISABLED
);
createTransitionForState
(
handler
,
AccountLockedException
.
class
.
getSimpleName
(),
CasWebflowConstants
.
VIEW_ID_ACCOUNT_LOCKED
);
// custo:
createTransitionForState
(
handler
,
AccountPasswordMustChangeException
.
class
.
getSimpleName
(),
"sendInstructions"
);
createTransitionForState
(
handler
,
AccountPasswordMustChangeException
.
class
.
getSimpleName
(),
CasWebflowConstants
.
VIEW_ID_SEND_RESET_PASSWORD_ACCT_INFO
);
createTransitionForState
(
handler
,
CredentialExpiredException
.
class
.
getSimpleName
(),
CasWebflowConstants
.
VIEW_ID_EXPIRED_PASSWORD
);
createTransitionForState
(
handler
,
InvalidLoginLocationException
.
class
.
getSimpleName
(),
CasWebflowConstants
.
VIEW_ID_INVALID_WORKSTATION
);
createTransitionForState
(
handler
,
InvalidLoginTimeException
.
class
.
getSimpleName
(),
CasWebflowConstants
.
VIEW_ID_INVALID_AUTHENTICATION_HOURS
);
...
...
cas/cas-server/src/main/resources/templates/casPropagateLogoutView.html
View file @
cc883b9d
...
...
@@ -81,7 +81,7 @@
<div>
<ol
start=
"a"
>
<ol
style=
"list-style: none"
start=
"a"
>
<li
th:each=
"entry,iterStat : ${logoutUrls}"
>
<script
type=
"text/javascript"
th:inline=
"javascript"
>
/*<![CDATA[*/
...
...
cas/cas-server/src/main/resources/templates/protocol/casGetResponseView.html
View file @
cc883b9d
...
...
@@ -5,7 +5,7 @@
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta
charset=
"UTF-8"
/>
<title
>
${@environment.getProperty('theme.vitamui-platform-name')}
</title>
<title
th:text=
"
${@environment.getProperty('theme.vitamui-platform-name')}
"
>
</title>
<meta
http-equiv=
"refresh"
th:attr=
"content=${'1; url=' + url}"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1"
>
...
...
commons/commons-rest/src/main/java/fr/gouv/vitamui/commons/rest/client/BaseWebClientFactory.java
View file @
cc883b9d
...
...
@@ -46,9 +46,6 @@ import java.security.KeyStoreException;
import
java.security.NoSuchAlgorithmException
;
import
java.security.cert.CertificateException
;
import
javax.net.ssl.KeyManagerFactory
;
import
javax.net.ssl.TrustManagerFactory
;
import
org.springframework.http.client.reactive.ClientHttpConnector
;
import
org.springframework.http.client.reactive.ReactorClientHttpConnector
;
import
org.springframework.util.Assert
;
...
...
@@ -56,6 +53,9 @@ import org.springframework.util.ResourceUtils;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.reactive.function.client.WebClient
;
import
javax.net.ssl.KeyManagerFactory
;
import
javax.net.ssl.TrustManagerFactory
;
import
fr.gouv.vitamui.commons.api.exception.ApplicationServerException
;
import
fr.gouv.vitamui.commons.api.logger.VitamUILogger
;
import
fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory
;
...
...
@@ -150,10 +150,16 @@ public class BaseWebClientFactory implements WebClientFactory {
SslContextBuilder
sslContextBuilder
=
SslContextBuilder
.
forClient
();
sslContextBuilder
=
sslContextBuilder
.
clientAuth
(
ClientAuth
.
NONE
);
if
(
restClientConfig
.
isNoClientAuthentication
())
{
LOGGER
.
warn
(
"By deactivating the authentication client we deprive ourselves of two-way authentication."
);
}
else
{
if
(
ks
!=
null
)
{
sslContextBuilder
=
sslContextBuilder
.
keyManager
(
createKeyManagerFactory
(
ks
.
getType
(),
ks
.
getKeyPath
(),
ks
.
getKeyPassword
().
toCharArray
()));
}
}
if
(
restClientConfig
.
getSslConfiguration
().
isHostnameVerification
())
{
final
TrustManagerFactory
tmfactory
=
createTrustManagerFactory
(
ts
.
getType
(),
ts
.
getKeyPath
(),
ts
.
getKeyPassword
().
toCharArray
());
sslContextBuilder
=
sslContextBuilder
.
sslProvider
(
SslProvider
.
JDK
).
trustManager
(
tmfactory
);
...
...
commons/commons-rest/src/main/java/fr/gouv/vitamui/commons/rest/client/configuration/RestClientConfiguration.java
View file @
cc883b9d
...
...
@@ -59,6 +59,8 @@ public class RestClientConfiguration {
private
boolean
secure
;
private
boolean
noClientAuthentication
=
false
;
private
SSLConfiguration
sslConfiguration
;
/**
...
...
pom.xml
View file @
cc883b9d
...
...
@@ -468,6 +468,30 @@
<scope>
test
</scope>
</dependency>
<!-- Apereo CAS Server Core Api Ticket -->
<dependency>
<groupId>
org.apereo.cas
</groupId>
<artifactId>
cas-server-core-api-ticket
</artifactId>
<version>
${cas.version}
</version>
<exclusions>
<exclusion>
<groupId>
org.springframework
</groupId>
<artifactId>
*
</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>
org.apereo.cas
</groupId>
<artifactId>
cas-server-core-tickets-api
</artifactId>
<version>
${cas.version}
</version>
<exclusions>
<exclusion>
<groupId>
org.springframework
</groupId>
<artifactId>
*
</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Web -->
<dependency>
<groupId>
javax.servlet
</groupId>
...
...
ui/ui-commons/src/main/java/fr/gouv/vitamui/ui/commons/service/ApplicationService.java
View file @
cc883b9d
...
...
@@ -163,7 +163,7 @@ public class ApplicationService extends AbstractCrudService<ApplicationDto> {
}
public
String
getBase64File
(
String
fileName
,
String
basePath
)
{
final
Path
assetFile
=
Paths
.
get
(
basePath
,
fileName
).
normalize
(
);
final
Path
assetFile
=
Paths
.
get
(
basePath
,
Paths
.
get
(
fileName
).
getFileName
().
toString
()
);
String
base64Asset
=
null
;
try
{
base64Asset
=
DatatypeConverter
.
printBase64Binary
(
Files
.
readAllBytes
(
assetFile
));
...
...
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