Compare commits
12 Commits
f187192567
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| fec580093b | |||
| c681dd9072 | |||
| 14093b1ce1 | |||
| 89b96fb053 | |||
| 947853479b | |||
| 6f520513a2 | |||
| cbe75da254 | |||
| 72cb8cf5ca | |||
| 91710bebbe | |||
| c96dbea004 | |||
| 72ef467d09 | |||
| e3067edf53 |
@@ -14,15 +14,13 @@ buildscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
subprojects {
|
||||||
group 'com.synebula'
|
group 'com.synebula'
|
||||||
version version
|
version version
|
||||||
}
|
|
||||||
|
|
||||||
subprojects {
|
|
||||||
ext {
|
ext {
|
||||||
version '0.9.0'
|
version '0.9.0'
|
||||||
gaea_version = '1.2.0'
|
gaea_version = '1.5.1'
|
||||||
spring_version = "2.7.0"
|
spring_version = "2.7.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
68
doc/db-init.js
Normal file
68
doc/db-init.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
db.role.insertMany([
|
||||||
|
{
|
||||||
|
_id: "admin",
|
||||||
|
name: "管理员"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "VE",
|
||||||
|
name: "Viewer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "RE",
|
||||||
|
name: "RE工程师"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "FE",
|
||||||
|
name: "FE工程师"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
db.group.insertOne(
|
||||||
|
{
|
||||||
|
_id: "1",
|
||||||
|
name: "管理员"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
db.user.insertOne({
|
||||||
|
name: "admin",
|
||||||
|
password: "1f22f6ce6e58a7326c5b5dd197973105",
|
||||||
|
realName: "管理员",
|
||||||
|
phone: "18654551561",
|
||||||
|
role: "admin",
|
||||||
|
group: "1",
|
||||||
|
alive: true,
|
||||||
|
_class: "com.synebula.zeus.domain.model.rbac.User"
|
||||||
|
});
|
||||||
|
|
||||||
|
db.system.insertOne({
|
||||||
|
_id: "1",
|
||||||
|
name: "web",
|
||||||
|
uri: "",
|
||||||
|
order: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
db.page.insertMany([
|
||||||
|
{ "_id": "1", "name": "groups", "uri": "/groups", "parent": "0", "system": "1", "order": 5, "icon": "group" },
|
||||||
|
{ "_id": "2", "name": "roles", "uri": "/roles", "parent": "0", "system": "1", "order": 6, "icon": "manual" },
|
||||||
|
{ "_id": "3", "name": "users", "uri": "/users", "parent": "0", "system": "1", "order": 7, "icon": "user" },
|
||||||
|
{ "_id": "4", "name": "sheet", "uri": "/repair/sheets/all", "parent": "0", "system": "1", "order": 1, "icon": "sheet" },
|
||||||
|
{ "_id": "5", "name": "sheet", "uri": "/repair/sheets/permission", "parent": "1", "system": "1", "order": 2, "icon": "sheet" },
|
||||||
|
{ "_id": "6", "name": "spare", "uri": "/repair/spares/all", "parent": "1", "system": "1", "order": 3, "icon": "setting" },
|
||||||
|
{ "_id": "7", "name": "spare", "uri": "/repair/spares/permission", "parent": "1", "system": "1", "order": 4, "icon": "setting" },
|
||||||
|
{ "_id": "8", "name": "manual", "uri": "/manual", "parent": "0", "system": "1", "order": 8, "icon": "manual" }
|
||||||
|
])
|
||||||
|
|
||||||
|
db.authority.insertMany([
|
||||||
|
{ "role": "admin", "resource": "1", "type": "System", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
{ "role": "admin", "resource": "1", "type": "Page", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
{ "role": "admin", "resource": "2", "type": "Page", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
{ "role": "admin", "resource": "3", "type": "Page", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
{ "role": "admin", "resource": "4", "type": "Page", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
{ "role": "admin", "resource": "5", "type": "Page", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
{ "role": "admin", "resource": "6", "type": "Page", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
{ "role": "admin", "resource": "7", "type": "Page", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
{ "role": "admin", "resource": "8", "type": "Page", "authority": "Allow", "alive": true, "_class": "com.synebula.zeus.domain.model.rbac.Authority" },
|
||||||
|
])
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ dependencies {
|
|||||||
api project(":src:zeus.domain")
|
api project(":src:zeus.domain")
|
||||||
api project(":src:zeus.query")
|
api project(":src:zeus.query")
|
||||||
api project(":src:zeus.repository")
|
api project(":src:zeus.repository")
|
||||||
api "com.synebula:gaea.app:$gaea_version"
|
api("com.synebula:gaea.app:$gaea_version")
|
||||||
api "com.synebula:gaea.spring:$gaea_version"
|
api "com.synebula:gaea.spring:$gaea_version"
|
||||||
api "com.synebula:gaea.mongodb:$gaea_version"
|
api "com.synebula:gaea.mongodb:$gaea_version"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
|
|||||||
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
open class Application
|
class Application
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
SpringApplication.run(Application::class.java, *args)
|
SpringApplication.run(Application::class.java, *args)
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import org.springframework.stereotype.Component
|
|||||||
@Component
|
@Component
|
||||||
class ZeusAspect : AppAspect() {
|
class ZeusAspect : AppAspect() {
|
||||||
|
|
||||||
@Pointcut("target(com.synebula.gaea.app.IApplication)")
|
/**
|
||||||
|
* 切片执行所有继承[com.synebula.gaea.app.controller.IApplication]接口的类
|
||||||
|
*/
|
||||||
|
@Pointcut("target(com.synebula.gaea.app.controller.IApplication)")
|
||||||
override fun func() {
|
override fun func() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,28 +1,18 @@
|
|||||||
package com.synebula.zeus.app.config
|
package com.synebula.zeus.app.config
|
||||||
|
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.synebula.gaea.app.component.security.WebSecurity
|
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||||
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
||||||
import com.synebula.gaea.domain.repository.IRepositoryFactory
|
import com.synebula.gaea.domain.repository.IRepositoryFactory
|
||||||
import com.synebula.gaea.mongodb.query.MongodbQueryFactory
|
import com.synebula.gaea.mongodb.query.MongodbQueryFactory
|
||||||
import com.synebula.gaea.mongodb.repository.MongodbRepositoryFactory
|
import com.synebula.gaea.mongodb.repository.MongodbRepositoryFactory
|
||||||
import com.synebula.gaea.query.IQueryFactory
|
import com.synebula.gaea.query.IQueryFactory
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.ComponentScan
|
|
||||||
import org.springframework.context.annotation.ComponentScan.Filter
|
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.context.annotation.FilterType
|
|
||||||
import org.springframework.data.mongodb.core.MongoTemplate
|
import org.springframework.data.mongodb.core.MongoTemplate
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(
|
|
||||||
basePackages = ["com.synebula.gaea.app.component"],
|
|
||||||
excludeFilters = [Filter(type = FilterType.ASSIGNABLE_TYPE, classes = [WebSecurity::class])]
|
|
||||||
)
|
|
||||||
class ZeusBeans {
|
class ZeusBeans {
|
||||||
|
|
||||||
@Bean
|
|
||||||
fun repoFactory(template: MongoTemplate): IRepositoryFactory {
|
fun repoFactory(template: MongoTemplate): IRepositoryFactory {
|
||||||
return MongodbRepositoryFactory(template)
|
return MongodbRepositoryFactory(template)
|
||||||
}
|
}
|
||||||
@@ -43,4 +33,9 @@ class ZeusBeans {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun httpMessageFactory(serializer: IJsonSerializer): HttpMessageFactory {
|
||||||
|
return HttpMessageFactory(serializer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.synebula.zeus.app.config
|
package com.synebula.zeus.app.config
|
||||||
|
|
||||||
import com.synebula.gaea.app.autoconfig.service.ServiceScan
|
import com.synebula.gaea.app.autoconfig.service.ServiceScan
|
||||||
import com.synebula.gaea.mongodb.autoconfig.MongodbRepoScan
|
import com.synebula.gaea.mongodb.autoconfig.MongodbRepositoryScan
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ServiceScan(basePackages = ["com.synebula.zeus.domain.service"])
|
@ServiceScan(basePackages = ["com.synebula.zeus.domain.service"])
|
||||||
@MongodbRepoScan(basePackages = ["com.synebula.zeus.domain.repository", "com.synebula.zeus.repository", "com.synebula.zeus.query"])
|
@MongodbRepositoryScan(basePackages = ["com.synebula.zeus.domain.repository", "com.synebula.zeus.repository", "com.synebula.zeus.query"])
|
||||||
class ZeusServices
|
class ZeusServices
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.synebula.zeus.app.controller
|
package com.synebula.zeus.app.controller
|
||||||
|
|
||||||
import com.synebula.gaea.app.IApplication
|
import com.synebula.gaea.app.controller.IApplication
|
||||||
import com.synebula.gaea.app.component.security.TokenManager
|
import com.synebula.gaea.app.security.session.UserSession
|
||||||
import com.synebula.gaea.app.struct.HttpMessage
|
import com.synebula.gaea.app.security.session.UserSessionManager
|
||||||
|
import com.synebula.gaea.data.message.HttpMessage
|
||||||
|
import com.synebula.gaea.data.message.HttpMessageFactory
|
||||||
import com.synebula.gaea.data.message.Status
|
import com.synebula.gaea.data.message.Status
|
||||||
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
@@ -11,10 +13,8 @@ import com.synebula.zeus.domain.service.cmd.rbac.UserCmd
|
|||||||
import com.synebula.zeus.domain.service.contr.rbac.IUserService
|
import com.synebula.zeus.domain.service.contr.rbac.IUserService
|
||||||
import com.synebula.zeus.query.contr.IUserQuery
|
import com.synebula.zeus.query.contr.IUserQuery
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
import org.springframework.web.bind.annotation.*
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/sign")
|
@RequestMapping("/sign")
|
||||||
@@ -27,11 +27,14 @@ class SignInOutApp(override var logger: ILogger) : IApplication {
|
|||||||
lateinit var userService: IUserService
|
lateinit var userService: IUserService
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
lateinit var tokenHelper: TokenManager
|
lateinit var userSessionManager: UserSessionManager
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
lateinit var serializer: IJsonSerializer
|
lateinit var serializer: IJsonSerializer
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
override lateinit var httpMessageFactory: HttpMessageFactory
|
||||||
|
|
||||||
override var name: String = "用户登录管理"
|
override var name: String = "用户登录管理"
|
||||||
|
|
||||||
@Method("用户登录")
|
@Method("用户登录")
|
||||||
@@ -40,20 +43,34 @@ class SignInOutApp(override var logger: ILogger) : IApplication {
|
|||||||
return this.safeExecute("用户登录出现异常") {
|
return this.safeExecute("用户登录出现异常") {
|
||||||
val message = this.userQuery.signIn(name, password)
|
val message = this.userQuery.signIn(name, password)
|
||||||
if (message.data != null) {
|
if (message.data != null) {
|
||||||
val user = message.data
|
val user = message.data!!
|
||||||
user!!.remember = remember ?: false
|
val token = userSessionManager.signIn(user.uid, user)
|
||||||
val token = tokenHelper.sign(message.data!!)
|
user.remember = remember ?: false
|
||||||
it.data = token
|
user.token = token
|
||||||
|
it.data = user
|
||||||
} else {
|
} else {
|
||||||
it.load(message)
|
it.load(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Method("登录用户信息")
|
||||||
|
@GetMapping("/user")
|
||||||
|
fun signUser(): HttpMessage {
|
||||||
|
val userSession = SecurityContextHolder.getContext().authentication.principal as UserSession
|
||||||
|
return httpMessageFactory.create(userSession.user)
|
||||||
|
}
|
||||||
|
|
||||||
@Method("用户登出")
|
@Method("用户登出")
|
||||||
@PostMapping("/out")
|
@PostMapping("/out")
|
||||||
fun signOut(user: String): HttpMessage {
|
fun signOut(): HttpMessage {
|
||||||
return HttpMessage(user)
|
val token = this.userSession()?.token
|
||||||
|
return if (token != null) {
|
||||||
|
userSessionManager.signOut(token)
|
||||||
|
this.httpMessageFactory.create(token)
|
||||||
|
} else
|
||||||
|
this.httpMessageFactory.create(Status.Unauthorized, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Method("用户注册")
|
@Method("用户注册")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.app.controller.rbac
|
package com.synebula.zeus.app.controller.rbac
|
||||||
|
|
||||||
import com.synebula.gaea.app.Application
|
import com.synebula.gaea.app.controller.Application
|
||||||
import com.synebula.gaea.app.struct.HttpMessage
|
import com.synebula.gaea.data.message.HttpMessage
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
import com.synebula.gaea.spring.aop.annotation.Method
|
import com.synebula.gaea.spring.aop.annotation.Method
|
||||||
import com.synebula.zeus.domain.service.cmd.rbac.AuthorityBatchAddCmd
|
import com.synebula.zeus.domain.service.cmd.rbac.AuthorityBatchAddCmd
|
||||||
@@ -25,7 +25,7 @@ class AuthorityApp(
|
|||||||
@PostMapping("/batch")
|
@PostMapping("/batch")
|
||||||
fun add(@RequestBody cmd: AuthorityBatchAddCmd): HttpMessage {
|
fun add(@RequestBody cmd: AuthorityBatchAddCmd): HttpMessage {
|
||||||
this.authorityService.add(cmd)
|
this.authorityService.add(cmd)
|
||||||
return HttpMessage()
|
return this.httpMessageFactory.create()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Method("根据资源和角色删除权限")
|
@Method("根据资源和角色删除权限")
|
||||||
@@ -36,6 +36,6 @@ class AuthorityApp(
|
|||||||
@RequestBody resource: List<String>
|
@RequestBody resource: List<String>
|
||||||
): HttpMessage {
|
): HttpMessage {
|
||||||
this.authorityService.removeByResourceRole(type, resource, role)
|
this.authorityService.removeByResourceRole(type, resource, role)
|
||||||
return HttpMessage()
|
return this.httpMessageFactory.create()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.synebula.zeus.app.controller.rbac
|
package com.synebula.zeus.app.controller.rbac
|
||||||
|
|
||||||
import com.synebula.gaea.app.Application
|
import com.synebula.gaea.app.controller.Application
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
import com.synebula.gaea.query.IQueryFactory
|
import com.synebula.gaea.query.IQueryFactory
|
||||||
import com.synebula.zeus.domain.service.cmd.rbac.GroupCmd
|
import com.synebula.zeus.domain.service.cmd.rbac.GroupCmd
|
||||||
@@ -16,5 +16,5 @@ class GroupApp(
|
|||||||
factory: IQueryFactory,
|
factory: IQueryFactory,
|
||||||
logger: ILogger
|
logger: ILogger
|
||||||
) : Application<GroupCmd, GroupView, String>(
|
) : Application<GroupCmd, GroupView, String>(
|
||||||
"分组信息", service, factory.createQuery(GroupView::class.java), logger
|
"用户组信息", service, factory.createQuery(GroupView::class.java), logger
|
||||||
)
|
)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.synebula.zeus.app.controller.rbac
|
package com.synebula.zeus.app.controller.rbac
|
||||||
|
|
||||||
import com.synebula.gaea.app.Application
|
import com.synebula.gaea.app.controller.Application
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
import com.synebula.gaea.query.IQueryFactory
|
import com.synebula.gaea.query.IQueryFactory
|
||||||
import com.synebula.zeus.domain.service.cmd.rbac.RoleCmd
|
import com.synebula.zeus.domain.service.cmd.rbac.RoleCmd
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.app.controller.rbac
|
package com.synebula.zeus.app.controller.rbac
|
||||||
|
|
||||||
import com.synebula.gaea.app.Application
|
import com.synebula.gaea.app.controller.Application
|
||||||
import com.synebula.gaea.app.struct.HttpMessage
|
import com.synebula.gaea.data.message.HttpMessage
|
||||||
import com.synebula.gaea.data.message.Status
|
import com.synebula.gaea.data.message.Status
|
||||||
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
import com.synebula.gaea.data.serialization.json.IJsonSerializer
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.app.controller.rbac.resource
|
package com.synebula.zeus.app.controller.rbac.resource
|
||||||
|
|
||||||
import com.synebula.gaea.app.Application
|
import com.synebula.gaea.app.controller.Application
|
||||||
import com.synebula.gaea.app.struct.HttpMessage
|
import com.synebula.gaea.data.message.HttpMessage
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
import com.synebula.gaea.spring.aop.annotation.Method
|
import com.synebula.gaea.spring.aop.annotation.Method
|
||||||
import com.synebula.zeus.domain.service.cmd.rbac.resource.InterfaceCmd
|
import com.synebula.zeus.domain.service.cmd.rbac.resource.InterfaceCmd
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.app.controller.rbac.resource
|
package com.synebula.zeus.app.controller.rbac.resource
|
||||||
|
|
||||||
import com.synebula.gaea.app.Application
|
import com.synebula.gaea.app.controller.Application
|
||||||
import com.synebula.gaea.app.struct.HttpMessage
|
import com.synebula.gaea.data.message.HttpMessage
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
import com.synebula.gaea.spring.aop.annotation.Method
|
import com.synebula.gaea.spring.aop.annotation.Method
|
||||||
import com.synebula.gaea.spring.aop.annotation.Module
|
import com.synebula.gaea.spring.aop.annotation.Module
|
||||||
@@ -28,7 +28,7 @@ class PageApp(
|
|||||||
@Method("获取角色系统下有权页面")
|
@Method("获取角色系统下有权页面")
|
||||||
@GetMapping("/in-system/{system}/authorized/{role}")
|
@GetMapping("/in-system/{system}/authorized/{role}")
|
||||||
fun authorized(@PathVariable system: String, @PathVariable role: String): HttpMessage {
|
fun authorized(@PathVariable system: String, @PathVariable role: String): HttpMessage {
|
||||||
val msg = HttpMessage()
|
val msg = this.httpMessageFactory.create()
|
||||||
msg.data = this.pageQuery.authorized(role, system)
|
msg.data = this.pageQuery.authorized(role, system)
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.app.controller.rbac.resource
|
package com.synebula.zeus.app.controller.rbac.resource
|
||||||
|
|
||||||
import com.synebula.gaea.app.Application
|
import com.synebula.gaea.app.controller.Application
|
||||||
import com.synebula.gaea.app.struct.HttpMessage
|
import com.synebula.gaea.data.message.HttpMessage
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
import com.synebula.gaea.spring.aop.annotation.Method
|
import com.synebula.gaea.spring.aop.annotation.Method
|
||||||
import com.synebula.zeus.domain.service.cmd.rbac.resource.SystemCmd
|
import com.synebula.zeus.domain.service.cmd.rbac.resource.SystemCmd
|
||||||
|
|||||||
@@ -4,6 +4,33 @@ server:
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: gaea.app
|
name: gaea.app
|
||||||
|
sign-in-url: /sign/in
|
||||||
|
allow-multi-sign: false
|
||||||
data:
|
data:
|
||||||
mongodb:
|
mongodb:
|
||||||
uri: mongodb://127.0.0.1/zeus
|
uri: mongodb://127.0.0.1/zeus
|
||||||
|
mail:
|
||||||
|
send: true
|
||||||
|
target: ge.com
|
||||||
|
sender: gehsrv@163.com
|
||||||
|
protocol: smtp
|
||||||
|
host: smtp.163.com
|
||||||
|
port: 465
|
||||||
|
username: gehsrv@163.com
|
||||||
|
password: SRBPJBLFFVVCPZLZ
|
||||||
|
default-encoding: UTF-8
|
||||||
|
properties:
|
||||||
|
mail:
|
||||||
|
imap:
|
||||||
|
ssl:
|
||||||
|
socketFactory:
|
||||||
|
fallback: false
|
||||||
|
smtp:
|
||||||
|
auth: true
|
||||||
|
ssl:
|
||||||
|
enable: true
|
||||||
|
socketFactory:
|
||||||
|
class: com.fintech.modules.base.util.mail.MailSSLSocketFactory
|
||||||
|
starttls:
|
||||||
|
enable: true
|
||||||
|
required: true
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<root level="warn">
|
<root level="info">
|
||||||
<appender-ref ref="file"/>
|
<appender-ref ref="console"/>
|
||||||
</root>
|
</root>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.domain.model.rbac
|
package com.synebula.zeus.domain.model.rbac
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.domain.model.AggregateRoot
|
import com.synebula.gaea.domain.model.AggregateRoot
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
|
|
||||||
class Authority(override var id: String? = null) : AggregateRoot<String>() {
|
class Authority(override var id: String? = null) : AggregateRoot<String>() {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.synebula.zeus.domain.model.rbac
|
package com.synebula.zeus.domain.model.rbac
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.PermissionType
|
||||||
import com.synebula.gaea.domain.model.AggregateRoot
|
import com.synebula.gaea.domain.model.AggregateRoot
|
||||||
|
|
||||||
class Role(override var id: String? = null) : AggregateRoot<String>() {
|
class Role(override var id: String? = null) : AggregateRoot<String>() {
|
||||||
var name = ""
|
var name = ""
|
||||||
var desc = ""
|
var desc = ""
|
||||||
|
var permissionType = PermissionType.Minimum
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.domain.service.cmd.rbac
|
package com.synebula.zeus.domain.service.cmd.rbac
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.domain.service.Command
|
import com.synebula.gaea.domain.service.Command
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
|
|
||||||
class AuthorityBatchAddCmd : Command() {
|
class AuthorityBatchAddCmd : Command() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.domain.service.cmd.rbac
|
package com.synebula.zeus.domain.service.cmd.rbac
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.domain.service.Command
|
import com.synebula.gaea.domain.service.Command
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
|
|
||||||
class AuthorityCmd : Command() {
|
class AuthorityCmd : Command() {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.synebula.zeus.domain.service.cmd.rbac
|
package com.synebula.zeus.domain.service.cmd.rbac
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.PermissionType
|
||||||
import com.synebula.gaea.domain.service.Command
|
import com.synebula.gaea.domain.service.Command
|
||||||
|
|
||||||
class RoleCmd : Command() {
|
class RoleCmd : Command() {
|
||||||
var id: String? = null
|
var id: String? = null
|
||||||
var name = ""
|
var name = ""
|
||||||
var desc = ""
|
var desc = ""
|
||||||
|
var permissionType = PermissionType.Minimum
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
package com.synebula.zeus.domain.service.impl.rbac
|
package com.synebula.zeus.domain.service.impl.rbac
|
||||||
|
|
||||||
|
import com.synebula.gaea.bus.DomainSubscribe
|
||||||
import com.synebula.gaea.data.message.DataMessage
|
import com.synebula.gaea.data.message.DataMessage
|
||||||
import com.synebula.gaea.data.message.Status
|
import com.synebula.gaea.data.message.Status
|
||||||
import com.synebula.gaea.data.serialization.IObjectMapper
|
import com.synebula.gaea.data.serialization.IObjectMapper
|
||||||
|
import com.synebula.gaea.domain.event.BeforeRemoveEvent
|
||||||
import com.synebula.gaea.domain.repository.IRepositoryFactory
|
import com.synebula.gaea.domain.repository.IRepositoryFactory
|
||||||
import com.synebula.gaea.domain.service.ICommand
|
import com.synebula.gaea.domain.service.ICommand
|
||||||
import com.synebula.gaea.domain.service.Service
|
import com.synebula.gaea.domain.service.Service
|
||||||
|
import com.synebula.gaea.exception.NoticeUserException
|
||||||
import com.synebula.gaea.ext.toMd5
|
import com.synebula.gaea.ext.toMd5
|
||||||
import com.synebula.gaea.log.ILogger
|
import com.synebula.gaea.log.ILogger
|
||||||
|
import com.synebula.zeus.domain.model.rbac.Group
|
||||||
|
import com.synebula.zeus.domain.model.rbac.Role
|
||||||
import com.synebula.zeus.domain.model.rbac.User
|
import com.synebula.zeus.domain.model.rbac.User
|
||||||
import com.synebula.zeus.domain.service.component.IUserNotifier
|
import com.synebula.zeus.domain.service.component.IUserNotifier
|
||||||
import com.synebula.zeus.domain.service.contr.rbac.IUserService
|
import com.synebula.zeus.domain.service.contr.rbac.IUserService
|
||||||
@@ -20,24 +25,17 @@ class UserService(
|
|||||||
var logger: ILogger
|
var logger: ILogger
|
||||||
) : Service<User, String>(User::class.java, factory.createRepository(User::class.java), mapper), IUserService {
|
) : Service<User, String>(User::class.java, factory.createRepository(User::class.java), mapper), IUserService {
|
||||||
|
|
||||||
// init {
|
@DomainSubscribe(domainClass = Role::class, messageClass = BeforeRemoveEvent::class)
|
||||||
// groupService.addBeforeRemoveListener(this.clazz.name) { id ->
|
fun beforeRoleRemove(event: BeforeRemoveEvent<Role, String>) {
|
||||||
// val msg = Message()
|
if (this.repository.count(mapOf(Pair("role", event.id!!))) > 0)
|
||||||
// if (this.repository.count(mapOf(Pair("group", id)), this.clazz) > 0) {
|
throw NoticeUserException("角色下还有用户")
|
||||||
// msg.status = Status.Failure
|
}
|
||||||
// msg.message = "组下还有用户"
|
|
||||||
// }
|
@DomainSubscribe(BeforeRemoveEvent::class, Group::class)
|
||||||
// msg
|
fun beforeGroupRemove(event: BeforeRemoveEvent<Group, String>) {
|
||||||
// }
|
if (this.repository.count(mapOf(Pair("group", event.id!!))) > 0)
|
||||||
// roleService.addBeforeRemoveListener(this.clazz.name) { id ->
|
throw NoticeUserException("用户组下还有用户")
|
||||||
// val msg = Message()
|
}
|
||||||
// if (this.repository.count(mapOf(Pair("role", id)), this.clazz) > 0) {
|
|
||||||
// msg.status = Status.Failure
|
|
||||||
// msg.message = "角色下还有用户"
|
|
||||||
// }
|
|
||||||
// msg
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
override fun add(command: ICommand): DataMessage<String> {
|
override fun add(command: ICommand): DataMessage<String> {
|
||||||
val user = this.map(command)
|
val user = this.map(command)
|
||||||
@@ -65,7 +63,7 @@ class UserService(
|
|||||||
this.repository.update(user)
|
this.repository.update(user)
|
||||||
DataMessage(Status.Success, "用户${user.name}激活成功")
|
DataMessage(Status.Success, "用户${user.name}激活成功")
|
||||||
} else {
|
} else {
|
||||||
logger.warn(this, "用户${user.name}激活失败, {key: ${key}, token: ${token}")
|
logger.warn(this, "用户${user.name}激活失败, {key: ${key}, token: $token")
|
||||||
DataMessage(Status.Failure, "用户${user.name}激活失败, 请从系统发送的邮件链接激活用户")
|
DataMessage(Status.Failure, "用户${user.name}激活失败, 请从系统发送的邮件链接激活用户")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +91,10 @@ class UserService(
|
|||||||
this.repository.update(user)
|
this.repository.update(user)
|
||||||
DataMessage()
|
DataMessage()
|
||||||
} else {
|
} else {
|
||||||
logger.warn(this, "用户重置${user.name}密码失败, 系统密码修改令牌:${user.token}, {key: ${key} , token: ${token}")
|
logger.warn(
|
||||||
|
this,
|
||||||
|
"用户重置${user.name}密码失败, 系统密码修改令牌:${user.token}, {key: $key , token: $token"
|
||||||
|
)
|
||||||
DataMessage(Status.Failure, "用户重置密码失败, 如需重置密码请从系统发送的邮件链接中重置")
|
DataMessage(Status.Failure, "用户重置密码失败, 如需重置密码请从系统发送的邮件链接中重置")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.synebula.zeus.env
|
|
||||||
|
|
||||||
enum class AuthorityType {
|
|
||||||
Default,
|
|
||||||
Deny,
|
|
||||||
Allow
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.synebula.zeus.env
|
package com.synebula.zeus.env
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源类型
|
||||||
|
*/
|
||||||
enum class ResourceType {
|
enum class ResourceType {
|
||||||
System,
|
System,
|
||||||
Page,
|
Page,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.query.contr
|
package com.synebula.zeus.query.contr
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.query.IQuery
|
import com.synebula.gaea.query.IQuery
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
import com.synebula.zeus.query.view.AuthorityView
|
import com.synebula.zeus.query.view.AuthorityView
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.query.contr.resouce
|
package com.synebula.zeus.query.contr.resouce
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.query.IQuery
|
import com.synebula.gaea.query.IQuery
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.query.view.resource.InterfaceView
|
import com.synebula.zeus.query.view.resource.InterfaceView
|
||||||
|
|
||||||
interface IInterfaceQuery : IQuery<InterfaceView, String> {
|
interface IInterfaceQuery : IQuery<InterfaceView, String> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.query.contr.resouce
|
package com.synebula.zeus.query.contr.resouce
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.query.IQuery
|
import com.synebula.gaea.query.IQuery
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.query.view.resource.PageView
|
import com.synebula.zeus.query.view.resource.PageView
|
||||||
|
|
||||||
interface IPageQuery : IQuery<PageView, String> {
|
interface IPageQuery : IQuery<PageView, String> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.query.contr.resouce
|
package com.synebula.zeus.query.contr.resouce
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.query.IQuery
|
import com.synebula.gaea.query.IQuery
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.query.view.resource.SystemView
|
import com.synebula.zeus.query.view.resource.SystemView
|
||||||
|
|
||||||
interface ISystemQuery : IQuery<SystemView, String> {
|
interface ISystemQuery : IQuery<SystemView, String> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.query.impl
|
package com.synebula.zeus.query.impl
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.mongodb.query.MongodbQuery
|
import com.synebula.gaea.mongodb.query.MongodbQuery
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
import com.synebula.zeus.query.contr.IAuthorityQuery
|
import com.synebula.zeus.query.contr.IAuthorityQuery
|
||||||
import com.synebula.zeus.query.view.AuthorityView
|
import com.synebula.zeus.query.view.AuthorityView
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.synebula.zeus.query.impl
|
|||||||
|
|
||||||
import com.synebula.gaea.data.message.DataMessage
|
import com.synebula.gaea.data.message.DataMessage
|
||||||
import com.synebula.gaea.data.message.Status
|
import com.synebula.gaea.data.message.Status
|
||||||
|
import com.synebula.gaea.data.permission.PermissionType
|
||||||
import com.synebula.gaea.ext.toMd5
|
import com.synebula.gaea.ext.toMd5
|
||||||
import com.synebula.gaea.mongodb.query.MongodbQuery
|
import com.synebula.gaea.mongodb.query.MongodbQuery
|
||||||
import com.synebula.gaea.mongodb.whereId
|
import com.synebula.gaea.mongodb.whereId
|
||||||
@@ -32,7 +33,8 @@ class UserQuery(template: MongoTemplate) :
|
|||||||
SignUserView(
|
SignUserView(
|
||||||
user.id, user.realName ?: "",
|
user.id, user.realName ?: "",
|
||||||
user.role ?: "", role?.name ?: "",
|
user.role ?: "", role?.name ?: "",
|
||||||
user.group ?: "", group?.name ?: ""
|
user.group ?: "", group?.name ?: "",
|
||||||
|
role?.permissionType ?: PermissionType.None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else
|
} else
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.query.impl.resouce
|
package com.synebula.zeus.query.impl.resouce
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.mongodb.query.MongodbQuery
|
import com.synebula.gaea.mongodb.query.MongodbQuery
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
import com.synebula.zeus.query.contr.IAuthorityQuery
|
import com.synebula.zeus.query.contr.IAuthorityQuery
|
||||||
import com.synebula.zeus.query.contr.resouce.IInterfaceQuery
|
import com.synebula.zeus.query.contr.resouce.IInterfaceQuery
|
||||||
@@ -26,7 +26,7 @@ class InterfaceQuery(
|
|||||||
if (authority == AuthorityType.Deny)
|
if (authority == AuthorityType.Deny)
|
||||||
return listOf()
|
return listOf()
|
||||||
}
|
}
|
||||||
val params = mutableMapOf<String, Any>()
|
val params = mutableMapOf<String, String>()
|
||||||
if (system != null) params["system"] = system
|
if (system != null) params["system"] = system
|
||||||
val interfaces = this.list(params)
|
val interfaces = this.list(params)
|
||||||
val authorities = this.authorityQuery.authorized(ResourceType.Interface, role)
|
val authorities = this.authorityQuery.authorized(ResourceType.Interface, role)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.query.impl.resouce
|
package com.synebula.zeus.query.impl.resouce
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.mongodb.query.MongodbQuery
|
import com.synebula.gaea.mongodb.query.MongodbQuery
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
import com.synebula.zeus.query.contr.IAuthorityQuery
|
import com.synebula.zeus.query.contr.IAuthorityQuery
|
||||||
import com.synebula.zeus.query.contr.resouce.IPageQuery
|
import com.synebula.zeus.query.contr.resouce.IPageQuery
|
||||||
@@ -24,7 +24,7 @@ class PageQuery(template: MongoTemplate, var authorityQuery: IAuthorityQuery, va
|
|||||||
if (authority == AuthorityType.Deny)
|
if (authority == AuthorityType.Deny)
|
||||||
return listOf()
|
return listOf()
|
||||||
}
|
}
|
||||||
val params = mutableMapOf<String, Any>()
|
val params = mutableMapOf<String, String>()
|
||||||
if (system != null) params["system"] = system
|
if (system != null) params["system"] = system
|
||||||
val pages = this.list(params)
|
val pages = this.list(params)
|
||||||
val authorities = this.authorityQuery.authorized(ResourceType.Page, role)
|
val authorities = this.authorityQuery.authorized(ResourceType.Page, role)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.synebula.zeus.query.impl.resouce
|
package com.synebula.zeus.query.impl.resouce
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.gaea.mongodb.query.MongodbQuery
|
import com.synebula.gaea.mongodb.query.MongodbQuery
|
||||||
import com.synebula.zeus.env.AuthorityType
|
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
import com.synebula.zeus.query.contr.IAuthorityQuery
|
import com.synebula.zeus.query.contr.IAuthorityQuery
|
||||||
import com.synebula.zeus.query.contr.resouce.ISystemQuery
|
import com.synebula.zeus.query.contr.resouce.ISystemQuery
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.synebula.zeus.query.view
|
package com.synebula.zeus.query.view
|
||||||
|
|
||||||
import com.synebula.zeus.env.AuthorityType
|
import com.synebula.gaea.data.permission.AuthorityType
|
||||||
import com.synebula.zeus.env.ResourceType
|
import com.synebula.zeus.env.ResourceType
|
||||||
|
|
||||||
class AuthorityView() {
|
class AuthorityView() {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import com.synebula.gaea.query.Where
|
|||||||
class GroupView {
|
class GroupView {
|
||||||
var id: String? = null
|
var id: String? = null
|
||||||
|
|
||||||
@Where(Operator.like)
|
@Where(Operator.Like)
|
||||||
var name = ""
|
var name = ""
|
||||||
|
|
||||||
var desc = ""
|
var desc = ""
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.synebula.zeus.query.view
|
package com.synebula.zeus.query.view
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.PermissionType
|
||||||
import com.synebula.gaea.query.Table
|
import com.synebula.gaea.query.Table
|
||||||
|
|
||||||
@Table("role")
|
@Table("role")
|
||||||
@@ -7,4 +8,5 @@ class RoleView {
|
|||||||
var id: String? = null
|
var id: String? = null
|
||||||
var name = ""
|
var name = ""
|
||||||
var desc = ""
|
var desc = ""
|
||||||
|
var permissionType = PermissionType.Minimum
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.synebula.zeus.query.view
|
package com.synebula.zeus.query.view
|
||||||
|
|
||||||
|
import com.synebula.gaea.data.permission.PermissionType
|
||||||
|
|
||||||
class SignUserView(
|
class SignUserView(
|
||||||
/**
|
/**
|
||||||
* 用户id
|
* 用户id
|
||||||
@@ -30,5 +32,7 @@ class SignUserView(
|
|||||||
* 组名称
|
* 组名称
|
||||||
*/
|
*/
|
||||||
var gname: String = "",
|
var gname: String = "",
|
||||||
var remember: Boolean = false
|
var permissionType: PermissionType = PermissionType.Minimum,
|
||||||
|
var remember: Boolean = false,
|
||||||
|
var token: String = ""
|
||||||
)
|
)
|
||||||
@@ -12,7 +12,7 @@ class UserView {
|
|||||||
|
|
||||||
var password: String = ""
|
var password: String = ""
|
||||||
|
|
||||||
@Where(Operator.like)
|
@Where(Operator.Like)
|
||||||
var realName: String? = null
|
var realName: String? = null
|
||||||
|
|
||||||
var phone: String? = null
|
var phone: String? = null
|
||||||
|
|||||||
Reference in New Issue
Block a user