Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
trismegisto-services
Overview
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
Mauro Paolo Josue Zuñiga Mallqui
trismegisto-services
Commits
ee037b7b
Commit
ee037b7b
authored
May 02, 2024
by
Mauro Paolo Josue Zuñiga Mallqui
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[EDIT] MODIFICACION EN EL USO DE TOKEN
parent
93e5a4ae
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
186 additions
and
71 deletions
+186
-71
JWTokenApi.java
...ava/web/multitask/trismegistoservices/api/JWTokenApi.java
+53
-16
MVCConfig.java
...a/web/multitask/trismegistoservices/config/MVCConfig.java
+3
-2
SecurityConfig.java
.../multitask/trismegistoservices/config/SecurityConfig.java
+11
-12
AuthChannelInterceptorAdapter.java
...megistoservices/filter/AuthChannelInterceptorAdapter.java
+0
-1
JWTokenFilter.java
...b/multitask/trismegistoservices/filter/JWTokenFilter.java
+1
-5
UserRepository.java
...titask/trismegistoservices/repository/UserRepository.java
+0
-16
TokenSingleton.java
...ltitask/trismegistoservices/singleton/TokenSingleton.java
+14
-4
JWTokenUtil.java
.../web/multitask/trismegistoservices/utils/JWTokenUtil.java
+102
-14
application.properties
src/main/resources/application.properties
+2
-1
No files found.
src/main/java/web/multitask/trismegistoservices/api/JWTokenApi.java
View file @
ee037b7b
package
web
.
multitask
.
trismegistoservices
.
api
;
import
lombok.AllArgsConstructor
;
import
org.json.JSONObject
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.security.core.userdetails.UserDetails
;
...
...
@@ -13,6 +12,7 @@ import web.multitask.trismegistoservices.utils.JWTokenUtil;
import
java.math.BigInteger
;
import
java.util.Objects
;
import
java.util.concurrent.atomic.AtomicBoolean
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
...
...
@@ -57,7 +57,7 @@ class JWTokenApi {
return
ResponseEntity
.
status
(
401
).
body
(
response
.
toMap
());
}
else
{
boolean
onelife
=
json
.
optBoolean
(
"onelife"
,
false
);
String
generatedToken
=
jwtTokenUtil
.
generateToken
((
User
)
userDetails
,
json
.
optBigInteger
(
"ms"
,
onelife
?
BigInteger
.
valueOf
(
0
)
:
BigInteger
.
valueOf
(
3600000
)));
String
generatedToken
=
jwtTokenUtil
.
generateToken
((
User
)
userDetails
,
json
.
optBigInteger
(
"ms"
,
onelife
?
BigInteger
.
valueOf
(
0
)
:
BigInteger
.
valueOf
(
3600000
))
,
onelife
);
if
(
onelife
){
tokenSingleton
.
addToken
(
generatedToken
);
}
...
...
@@ -74,8 +74,16 @@ class JWTokenApi {
JSONObject
json
=
new
JSONObject
(
token
);
JSONObject
response
=
new
JSONObject
();
if
(!
jwtTokenUtil
.
validateToken
(
json
.
getString
(
"token"
)))
{
response
.
put
(
"message"
,
"Invalid Token"
).
put
(
"status"
,
false
);
boolean
oneLifeToken
=
tokenSingleton
.
isTokenAvailable
(
json
.
getString
(
"token"
));
if
(
oneLifeToken
){
response
.
put
(
"message"
,
"1-life Token is still up"
).
put
(
"status"
,
true
);
return
ResponseEntity
.
ok
(
response
.
toMap
());
}
boolean
validToken
=
jwtTokenUtil
.
validateToken
(
json
.
getString
(
"token"
));
if
(!
validToken
)
{
response
.
put
(
"message"
,
"This token doesn't exist, it might be created on malicious purposes"
).
put
(
"status"
,
false
);
return
ResponseEntity
.
status
(
401
).
body
(
response
.
toMap
());
}
...
...
@@ -91,17 +99,6 @@ class JWTokenApi {
return
ResponseEntity
.
status
(
403
).
body
(
response
.
toMap
());
}
try
{
UserDetails
userDetails
=
userRepo
.
findByUsername
(
new
JSONObject
(
dataToken
).
getString
(
"username"
));
if
(
userDetails
==
null
)
{
response
.
put
(
"message"
,
"Invalid Token"
).
put
(
"status"
,
false
);
return
ResponseEntity
.
status
(
401
).
body
(
response
.
toMap
());
}
}
catch
(
Exception
e
)
{
response
.
put
(
"message"
,
"Invalid Token"
).
put
(
"status"
,
false
);
return
ResponseEntity
.
status
(
401
).
body
(
response
.
toMap
());
}
response
.
put
(
"message"
,
"Valid Token"
).
put
(
"status"
,
true
);
return
ResponseEntity
.
ok
(
response
.
toMap
());
}
...
...
@@ -114,7 +111,7 @@ class JWTokenApi {
return
ResponseEntity
.
status
(
401
).
body
(
new
JSONObject
().
put
(
"token"
,
""
).
put
(
"message"
,
"Invalid Credentials"
).
put
(
"status"
,
false
).
toMap
());
}
else
if
(
userDetails
.
getAuthorities
().
stream
().
anyMatch
(
a
->
a
.
getAuthority
().
equals
(
"SERVICE"
)))
{
boolean
onelife
=
json
.
optBoolean
(
"onelife"
,
false
);
String
generatedToken
=
jwtTokenUtil
.
generateToken
((
User
)
userDetails
,
json
.
optBigInteger
(
"ms"
,
onelife
?
BigInteger
.
valueOf
(
0
)
:
BigInteger
.
valueOf
(
3600000
)));
String
generatedToken
=
jwtTokenUtil
.
generateToken
((
User
)
userDetails
,
json
.
optBigInteger
(
"ms"
,
onelife
?
BigInteger
.
valueOf
(
0
)
:
BigInteger
.
valueOf
(
3600000
))
,
false
);
if
(
onelife
){
tokenSingleton
.
addToken
(
generatedToken
);
}
...
...
@@ -135,4 +132,43 @@ class JWTokenApi {
}
}
@PostMapping
(
"/tokenize"
)
public
ResponseEntity
<?>
tokenize
(
@RequestBody
String
data
)
{
JSONObject
json
=
new
JSONObject
(
data
);
try
{
boolean
onelife
=
json
.
optBoolean
(
"onelife"
,
false
);
String
tokenized
=
jwtTokenUtil
.
tokenizeData
(
data
,
onelife
?
BigInteger
.
valueOf
(
0
)
:
BigInteger
.
valueOf
(
3600000
),
onelife
);
if
(
onelife
){
tokenSingleton
.
addToken
(
tokenized
);
}
return
ResponseEntity
.
ok
(
new
JSONObject
().
put
(
"token"
,
tokenized
).
put
(
"message"
,
"OK"
).
put
(
"status"
,
true
).
toMap
());
}
catch
(
Exception
e
)
{
return
ResponseEntity
.
status
(
400
).
body
(
new
JSONObject
().
put
(
"token"
,
""
).
put
(
"message"
,
e
.
getMessage
()).
put
(
"status"
,
false
).
toMap
());
}
}
@PostMapping
(
"/detokenize"
)
public
ResponseEntity
<?>
detokenize
(
@RequestBody
String
token
)
{
JSONObject
json
=
new
JSONObject
(
token
);
try
{
String
detokenized
=
jwtTokenUtil
.
detokenizeData
(
json
.
getString
(
"token"
));
return
ResponseEntity
.
ok
(
new
JSONObject
().
put
(
"data"
,
detokenized
).
put
(
"message"
,
"OK"
).
put
(
"status"
,
true
).
toMap
());
}
catch
(
Exception
e
)
{
return
ResponseEntity
.
status
(
400
).
body
(
new
JSONObject
().
put
(
"data"
,
""
).
put
(
"message"
,
e
.
getMessage
()).
put
(
"status"
,
false
).
toMap
());
}
}
@PostMapping
(
"/consume"
)
public
ResponseEntity
<?>
consumeToken
(
@RequestBody
String
token
)
{
JSONObject
json
=
new
JSONObject
(
token
);
try
{
if
(
tokenSingleton
.
consumeToken
(
json
.
getString
(
"token"
))){
return
ResponseEntity
.
ok
(
new
JSONObject
().
put
(
"message"
,
"OK"
).
put
(
"status"
,
true
).
toMap
());
}
else
{
return
ResponseEntity
.
status
(
400
).
body
(
new
JSONObject
().
put
(
"message"
,
"Invalid Token"
).
put
(
"status"
,
false
).
toMap
());
}
}
catch
(
Exception
e
)
{
return
ResponseEntity
.
status
(
400
).
body
(
new
JSONObject
().
put
(
"message"
,
e
.
getMessage
()).
put
(
"status"
,
false
).
toMap
());
}
}
}
\ No newline at end of file
src/main/java/web/multitask/trismegistoservices/config/MVCConfig.java
View file @
ee037b7b
...
...
@@ -10,7 +10,7 @@ public class MVCConfig
implements
WebMvcConfigurer
{
@Override
public
void
addCorsMappings
(
@NonNull
CorsRegistry
registry
)
{
//
registry.addMapping("/**")
//
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
registry
.
addMapping
(
"/**"
)
.
allowedMethods
(
"HEAD"
,
"GET"
,
"PUT"
,
"POST"
,
"DELETE"
,
"PATCH"
);
}
}
\ No newline at end of file
src/main/java/web/multitask/trismegistoservices/config/SecurityConfig.java
View file @
ee037b7b
...
...
@@ -18,7 +18,6 @@ import org.springframework.web.cors.CorsConfiguration;
import
org.springframework.web.cors.UrlBasedCorsConfigurationSource
;
import
org.springframework.web.filter.CorsFilter
;
import
web.multitask.trismegistoservices.filter.JWTokenFilter
;
import
web.multitask.trismegistoservices.repository.UserRepository
;
import
web.multitask.trismegistoservices.singleton.ThreadLocalSingleton
;
import
web.multitask.trismegistoservices.singleton.TokenSingleton
;
import
web.multitask.trismegistoservices.utils.JWTokenUtil
;
...
...
@@ -28,7 +27,6 @@ import web.multitask.trismegistoservices.utils.JWTokenUtil;
@AllArgsConstructor
public
class
SecurityConfig
{
// private final UserRepository userRepo;
private
final
JWTokenUtil
jwtTokenUtil
;
private
final
TokenSingleton
tokenSingleton
;
private
ThreadLocalSingleton
threadLocalSingleton
;
...
...
@@ -60,15 +58,15 @@ public class SecurityConfig{
return
new
BCryptPasswordEncoder
();
}
//
@Bean
//
CorsFilter corsFilter() {
//
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//
CorsConfiguration config = new CorsConfiguration();
//
config.addAllowedOrigin("*");
//
config.addAllowedHeader("*");
//
config.addAllowedMethod("*");
//
source.registerCorsConfiguration("/**", config);
//
return new CorsFilter(source);
//
}
@Bean
CorsFilter
corsFilter
()
{
UrlBasedCorsConfigurationSource
source
=
new
UrlBasedCorsConfigurationSource
();
CorsConfiguration
config
=
new
CorsConfiguration
();
config
.
addAllowedOrigin
(
"*"
);
config
.
addAllowedHeader
(
"*"
);
config
.
addAllowedMethod
(
"*"
);
source
.
registerCorsConfiguration
(
"/**"
,
config
);
return
new
CorsFilter
(
source
);
}
}
\ No newline at end of file
src/main/java/web/multitask/trismegistoservices/filter/AuthChannelInterceptorAdapter.java
View file @
ee037b7b
...
...
@@ -21,7 +21,6 @@ public class AuthChannelInterceptorAdapter implements ChannelInterceptor {
private
final
JWTokenUtil
jwtTokenUtil
;
private
final
UserRepository
userRepo
=
new
UserRepository
();
public
AuthChannelInterceptorAdapter
(
JWTokenUtil
jwtTokenUtil
)
{
this
.
jwtTokenUtil
=
jwtTokenUtil
;
}
...
...
src/main/java/web/multitask/trismegistoservices/filter/JWTokenFilter.java
View file @
ee037b7b
...
...
@@ -30,13 +30,10 @@ import web.multitask.trismegistoservices.utils.JWTokenUtil;
@Component
@Order
(
1
)
//@AllArgsConstructor
//
@NoArgsConstructor
@NoArgsConstructor
public
class
JWTokenFilter
extends
OncePerRequestFilter
{
public
JWTokenFilter
(){}
private
final
UserRepository
userRepo
=
new
UserRepository
();
private
JWTokenUtil
jwtTokenUtil
=
null
;
private
TokenSingleton
tokenSingleton
=
null
;
private
ThreadLocalSingleton
threadLocalSingleton
=
null
;
...
...
@@ -47,7 +44,6 @@ public class JWTokenFilter extends OncePerRequestFilter {
this
.
threadLocalSingleton
=
threadLocalSingleton
;
}
@Override
protected
void
doFilterInternal
(
HttpServletRequest
request
,
@NotNull
HttpServletResponse
response
,
@NotNull
FilterChain
chain
)
throws
ServletException
,
IOException
,
java
.
io
.
IOException
{
...
...
src/main/java/web/multitask/trismegistoservices/repository/UserRepository.java
View file @
ee037b7b
package
web
.
multitask
.
trismegistoservices
.
repository
;
//@Repository
//public interface UserRepository extends JpaRepository<User, Long> {
//
// UserDetails findByUsername(String username)
// throws UsernameNotFoundException;
//
//}
import
org.json.JSONArray
;
import
org.json.JSONObject
;
import
org.springframework.security.core.userdetails.UserDetails
;
import
web.multitask.trismegistoservices.model.Role
;
import
web.multitask.trismegistoservices.model.User
;
...
...
@@ -38,9 +28,4 @@ public class UserRepository{
return
users
.
get
(
username
);
}
public
static
void
main
(
String
[]
args
)
{
UserRepository
userRepository
=
new
UserRepository
();
System
.
out
.
println
(
userRepository
.
findByUsername
(
"admin"
).
getAuthorities
());
}
}
\ No newline at end of file
src/main/java/web/multitask/trismegistoservices/singleton/TokenSingleton.java
View file @
ee037b7b
package
web
.
multitask
.
trismegistoservices
.
singleton
;
import
lombok.Getter
;
import
lombok.NoArgsConstructor
;
import
org.json.JSONArray
;
import
org.json.JSONObject
;
import
org.springframework.scheduling.annotation.Scheduled
;
...
...
@@ -10,6 +11,7 @@ import java.util.stream.IntStream;
@Getter
@Component
@NoArgsConstructor
public
class
TokenSingleton
{
private
final
JSONArray
tokens
=
new
JSONArray
();
...
...
@@ -28,19 +30,27 @@ public class TokenSingleton {
return
isAvailable
;
}
public
boolean
isTokenAvailable
(
String
token
)
{
boolean
isAvailable
=
false
;
for
(
int
i
=
0
;
i
<
tokens
.
length
();
i
++)
{
if
(
tokens
.
getJSONObject
(
i
).
getString
(
"token"
).
equals
(
token
))
{
isAvailable
=
tokens
.
getJSONObject
(
i
).
getBoolean
(
"available"
);
break
;
}
}
return
isAvailable
;
}
public
void
addToken
(
String
token
)
{
tokens
.
put
(
new
JSONObject
().
put
(
"token"
,
token
).
put
(
"available"
,
true
));
}
public
TokenSingleton
()
{
}
@Scheduled
(
fixedRate
=
3600000
)
public
void
removeUnavailableTokens
()
{
System
.
out
.
println
(
"Removing unavailable tokens"
);
IntStream
.
range
(
0
,
tokens
.
length
()).
forEach
(
i
->
{
if
(!
tokens
.
getJSONObject
(
i
).
getBoolean
(
"available"
))
{
tokens
.
remove
(
i
);
System
.
out
.
println
(
tokens
.
remove
(
i
)
);
}
});
}
...
...
src/main/java/web/multitask/trismegistoservices/utils/JWTokenUtil.java
View file @
ee037b7b
...
...
@@ -2,6 +2,7 @@ package web.multitask.trismegistoservices.utils;
import
java.io.Serializable
;
import
java.math.BigInteger
;
import
java.util.Arrays
;
import
java.util.Date
;
import
io.jsonwebtoken.ExpiredJwtException
;
...
...
@@ -12,14 +13,18 @@ import io.jsonwebtoken.Jwts;
import
io.jsonwebtoken.security.Keys
;
import
org.springframework.stereotype.Component
;
import
web.multitask.trismegistoservices.model.User
;
import
web.multitask.trismegistoservices.singleton.TokenSingleton
;
@Component
public
class
JWTokenUtil
implements
Serializable
{
@Value
(
"${app.jwtSecret}"
)
private
String
jwtSecret
;
@Value
(
"${app.jwtSecret2}"
)
private
String
jwtSecret2
;
TokenSingleton
tokenSingleton
=
new
TokenSingleton
();
public
String
generateToken
(
User
user
,
BigInteger
ms
)
{
public
String
generateToken
(
User
user
,
BigInteger
ms
,
boolean
onelife
)
{
if
(
ms
==
null
){
ms
=
BigInteger
.
valueOf
(
3600000
);
}
...
...
@@ -31,17 +36,58 @@ public class JWTokenUtil implements Serializable{
.
setSubject
(
json
.
toString
())
.
setIssuedAt
(
new
Date
())
.
setExpiration
(
expiryDate
)
.
signWith
(
Keys
.
hmacShaKeyFor
(
jwtSecret
.
getBytes
()))
.
signWith
(
Keys
.
hmacShaKeyFor
(
onelife
?
jwtSecret2
.
getBytes
()
:
jwtSecret
.
getBytes
()))
.
compact
();
}
public
String
generateDataSource
(
JSONObject
json
){
public
String
tokenizeData
(
String
data
,
BigInteger
ms
,
boolean
onelife
){
return
Jwts
.
builder
()
.
setSubject
(
data
)
.
setIssuedAt
(
new
Date
())
.
setExpiration
(
new
Date
(
new
Date
().
getTime
()
+
36000000
))
.
signWith
(
Keys
.
hmacShaKeyFor
(
onelife
?
jwtSecret2
.
getBytes
()
:
jwtSecret
.
getBytes
()))
.
compact
();
}
public
String
detokenizeData
(
String
token
){
String
tokenReturned
=
""
;
try
{
tokenReturned
=
Jwts
.
parserBuilder
()
.
setSigningKey
(
Keys
.
hmacShaKeyFor
(
jwtSecret
.
getBytes
()))
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
()
.
getSubject
();
}
catch
(
Exception
e
){
try
{
tokenReturned
=
Jwts
.
parserBuilder
()
.
setSigningKey
(
Keys
.
hmacShaKeyFor
(
jwtSecret2
.
getBytes
()))
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
()
.
getSubject
();
}
catch
(
Exception
e2
){
System
.
out
.
println
(
e2
.
getMessage
());
}
}
return
tokenReturned
;
}
public
String
generateDataSource
(
JSONObject
json
){
String
dataReturned
=
""
;
try
{
dataReturned
=
Jwts
.
builder
()
.
setSubject
(
json
.
toString
())
.
setIssuedAt
(
new
Date
())
.
setExpiration
(
new
Date
(
new
Date
().
getTime
()
+
36000000
))
.
signWith
(
Keys
.
hmacShaKeyFor
(
jwtSecret
.
getBytes
()))
.
compact
();
}
catch
(
Exception
e
){
System
.
out
.
println
(
e
.
getMessage
());
}
return
dataReturned
;
}
public
boolean
validateToken
(
String
token
)
{
...
...
@@ -52,7 +98,8 @@ public class JWTokenUtil implements Serializable{
.
parseClaimsJws
(
token
);
return
true
;
}
catch
(
Exception
e
)
{
return
false
;
System
.
out
.
println
(
e
.
getMessage
());
return
false
;
}
}
...
...
@@ -66,22 +113,48 @@ public class JWTokenUtil implements Serializable{
}
catch
(
ExpiredJwtException
expiredJwtException
)
{
return
expiredJwtException
.
getClaims
().
getSubject
();
}
catch
(
Exception
e
)
{
System
.
out
.
println
(
e
.
getMessage
());
return
null
;
try
{
return
Jwts
.
parserBuilder
()
.
setSigningKey
(
Keys
.
hmacShaKeyFor
(
jwtSecret2
.
getBytes
()))
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
().
getSubject
();
}
catch
(
Exception
e2
){
System
.
out
.
println
(
e2
.
getMessage
());
return
null
;
}
}
}
public
boolean
isTokenExpired
(
String
token
)
{
return
Jwts
.
parserBuilder
()
.
setSigningKey
(
Keys
.
hmacShaKeyFor
(
jwtSecret
.
getBytes
()))
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
()
.
getExpiration
()
.
before
(
new
Date
());
try
{
Jwts
.
parserBuilder
()
.
setSigningKey
(
Keys
.
hmacShaKeyFor
(
jwtSecret
.
getBytes
()))
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
()
.
getExpiration
()
.
before
(
new
Date
());
return
false
;
}
catch
(
Exception
e
)
{
try
{
Jwts
.
parserBuilder
()
.
setSigningKey
(
Keys
.
hmacShaKeyFor
(
jwtSecret2
.
getBytes
()))
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
()
.
getExpiration
()
.
before
(
new
Date
());
return
false
;
}
catch
(
Exception
e2
)
{
System
.
out
.
println
(
e2
.
getMessage
());
return
true
;
}
}
}
public
int
getExperyTime
(
String
token
){
try
{
return
(
int
)
((
Jwts
.
parserBuilder
()
.
setSigningKey
(
Keys
.
hmacShaKeyFor
(
jwtSecret
.
getBytes
()))
.
build
()
...
...
@@ -89,5 +162,19 @@ public class JWTokenUtil implements Serializable{
.
getBody
()
.
getExpiration
()
.
getTime
()
-
new
Date
().
getTime
()));
}
catch
(
Exception
e
){
try
{
return
(
int
)
((
Jwts
.
parserBuilder
()
.
setSigningKey
(
Keys
.
hmacShaKeyFor
(
jwtSecret2
.
getBytes
()))
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
()
.
getExpiration
()
.
getTime
()
-
new
Date
().
getTime
()));
}
catch
(
Exception
e2
){
System
.
out
.
println
(
e2
.
getMessage
());
return
0
;
}
}
}
}
\ No newline at end of file
src/main/resources/application.properties
View file @
ee037b7b
...
...
@@ -3,10 +3,11 @@
#spring.datasource.password=5vC0$2019$
#spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.autoconfigure.exclude
=
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
server.port
=
808
0
server.port
=
808
1
server.address
=
0.0.0.0
# spring.jpa.show-sql=true
app.jwtSecret
=
9a4f2c8d3b7a1e6f45c8a0b3f267d8b1d4e6f3c8a9d2b5f8e3a9c8b5f6v8a3d9
app.jwtSecret2
=
9a4f2c8d3b7a1e6f45c8a0b3f267d8b1d4e6f3c8a9d2b5f8e3a9c8b5f6v8a3d8
spring.jpa.hibernate.ddl-auto
=
update
spring.security.filter.order
=
1
spring.servlet.multipart.max-file-size
=
100MB
...
...
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