save-cloud
save-cloud copied to clipboard
Enhance authorization and authentication in save-cloud
TODO:
- [x] test API after changing
- https://github.com/saveourtool/save-backend-tests/pull/71
- [x] #2386
- refactor
BackendServiceand receive save's entity\class instead of spring's one to allow to provide ID - refactor
api-gatewayandauthentication-serviceto pass user's ID from api-gateway instead of second fetch to database inauthentication-service's logic - removed using
Authentication: Basic ***:***inauthentication-service-- we can create save's token with required info without checkingReactiveUserDetailsService. We can try to useorg.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken. It can be easy to migrate to X.509 authentication in future
- refactor
- [ ] Update swagger\open-api annotations:
- remove
com.saveourtool.save.configs.RequiresAuthorizationSourceHeaderor maybe leave because it's useful for local testing - update
com.saveourtool.save.configs.ApiSwaggerSupport, we support oauth2 and basic auth
- remove
- [ ] cover by tests
- [ ] #2407
- [x] #2631
- [x] #2629
- [x] #2633
- [ ] #2540
Separator for user and auth provider should be revised
For now we are using @ for separate the user name and auth provider at least in WebSecurityConfig and in UserUtils.kt
But this symbol is not the variable and just the hardcoded symbol, so its hard to maintain this functionality
First of all, it should be extracted into the variable in save-cloud-common
And after this, we probably will need to change @ to something else, since @ is too dangerous to be a spec symbol
This work should be done under separate issue and very carefully, since it influence on core functionality - API application, auth. integration tests, db
CRITICAL thing we have just found:
- User VLAD comes from Github
- Initially spring register him as VLAD@GITHUB
- Then he is redirected to a registration view, where he CAN change VLAD name to ANDREY
- ANDREY comes from GITHUB
- Now ANDREY is not even able to press OAuth registration button
That's why we need to GENERATE a our own SAVE ID for user when he first time register!
seems, that @ should not influent on problems with email at all, since its only used in basic auth, but should be extracted anyway
.httpBasic { httpBasicSpec ->
// Authenticate by comparing received basic credentials with existing one from DB
httpBasicSpec.authenticationManager(
UserDetailsRepositoryReactiveAuthenticationManager { username ->
// Looking for user in DB by received source and name
require(username.contains("@")) {
"Provided user information should keep the following form: oauth2Source@username"
}
Also be careful with Basic oauth
That's why we need to GENERATE a our own SAVE ID for user when he first time register!
fun findByName(name: String): User? {
val record = namedParameterJdbcTemplate.queryForList(
"SELECT * FROM save_cloud.user WHERE name = :name",
mapOf("name" to name)
).singleOrNull() ?:
namedParameterJdbcTemplate.queryForList(
"SELECT * FROM save_cloud.user WHERE id = (select user_id from save_cloud.original_login where name = :name)",
mapOf("name" to name)
).singleOrNull()
.orNotFound {
"There is no user with name $name"
}
return record.toUserEntity()
}
For history
A&A in save-cloud:
Basic auth:
- Request comes to api-gateway with header
Authorization: Basic ***:*** - api-gateway has
UserDetailsRepositoryReactiveAuthenticationManager(com.saveourtool.save.gateway.security.WebSecurityConfig) which uses internal endpoint in backend to get all entities fromuserwithuser.name,user.password(token) anduser.role. - Authenticated request comes then to
com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactorywhich adds a new headerAuthorizationto request to backend. It passes only user name without password. - Backend uses
com.saveourtool.save.authservice.security.ConvertingAuthenticationManagerto authenticate: it checks only username
OAUTH2
- Request comes to api-gateway after successful authentication on oauth2 server (github, google and etc).
- Authenticated request comes then to
com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactorywhich searchesuser.namevia internal endpoint in backend and adds a new headerAuthorizationto request to backend. It passes only user name without password. - Backend uses
com.saveourtool.save.authservice.security.ConvertingAuthenticationManagerto authenticate: it checks only username
For history
A&A in save-cloud:
Basic auth:
- Request comes to api-gateway with header
Authorization: Basic ***:***- api-gateway has
UserDetailsRepositoryReactiveAuthenticationManager(com.saveourtool.save.gateway.security.WebSecurityConfig) which uses internal endpoint in backend to get all entities fromuserwithuser.name,user.password(token) anduser.role.- Authenticated request comes then to
com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactorywhich adds a new headerAuthorizationto request to backend. It passes only user name without password.- Backend uses
com.saveourtool.save.authservice.security.ConvertingAuthenticationManagerto authenticate: it checks only usernameOAUTH2
- Request comes to api-gateway after successful authentication on oauth2 server (github, google and etc).
- Authenticated request comes then to
com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactorywhich searchesuser.namevia internal endpoint in backend and adds a new headerAuthorizationto request to backend. It passes only user name without password.- Backend uses
com.saveourtool.save.authservice.security.ConvertingAuthenticationManagerto authenticate: it checks only username
may be add in readme, into some dev guide?
For history
A&A in save-cloud:
Basic auth:
- Request comes to api-gateway with header
Authorization: Basic ***:***- api-gateway has
UserDetailsRepositoryReactiveAuthenticationManager(com.saveourtool.save.gateway.security.WebSecurityConfig) which uses internal endpoint in backend to get all entities fromuserwithuser.name,user.password(token) anduser.role.- Authenticated request comes then to
com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactorywhich adds a new headerAuthorizationto request to backend. It passes only user name without password.- Backend uses
com.saveourtool.save.authservice.security.ConvertingAuthenticationManagerto authenticate: it checks only usernameOAUTH2
- Request comes to api-gateway after successful authentication on oauth2 server (github, google and etc).
- Authenticated request comes then to
com.saveourtool.save.gateway.utils.ConvertAuthorizationHeaderGatewayFilterFactorywhich searchesuser.namevia internal endpoint in backend and adds a new headerAuthorizationto request to backend. It passes only user name without password.- Backend uses
com.saveourtool.save.authservice.security.ConvertingAuthenticationManagerto authenticate: it checks only usernamemay be add in readme, into some dev guide?
will do, when finish with this issue
TODO:
- [ ] test API after changing
- [ ] refactor
BackendServiceand receive save's entity\class instead of spring's one to allow to provide ID - [ ] refactor
api-gatewayandauthentication-serviceto pass user's ID from api-gateway instead of second fetch to database inauthentication-service's logic - [ ] removed using
Authentication: Basic ***:***inauthentication-service-- we can create save's token with required info without checkingReactiveUserDetailsService. We can try to useorg.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken. It can be easy to migrate to X.509 authentication in future. Spring boot doesn't support it out of box. Need to implement customWebFilter - [ ] cover by tests
- [ ] update readme