Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
yecl
wecube
Commits
b1dcc9f2
Commit
b1dcc9f2
authored
5 years ago
by
Haixin HUANG(黄海新)
Committed by
GitHub
5 years ago
Browse files
Options
Download
Plain Diff
Merge pull request #1364 from WeBankPartners/1363_menu_config_sync
#1363 menu config sync
parents
a4c8810b
e554e5e8
master
2109_support_taskman
2154_enhance_workflow_data_preview
2189_fix_system_bug
2222_deployment_confirmation
2230_plugin_invocation_confirm_token
2233_workflow_context_parameters_refactor
2289_workflow_interf_enhance
2313_add_timestamp_to_node_log
2317_fix_bug_of_faulted_proc_inst
2321_fix_itsm_reported_defects
2321_fix_itsm_reported_issues
dependabot/maven/platform-auth-client/org.bouncycastle-bcprov-ext-jdk15on-1.67
dependabot/maven/platform-auth-client/org.bouncycastle-bcprov-jdk15on-1.67
dependabot/maven/platform-auth-server/commons-io-commons-io-2.7
dependabot/maven/platform-auth-server/org.bouncycastle-bcprov-jdk15on-1.67
dependabot/maven/platform-core/com.google.guava-guava-29.0-jre
dependabot/maven/platform-core/commons-io-commons-io-2.7
dependabot/maven/platform-workflow/commons-io-commons-io-2.7
dev
dev_fix
dev_test
flow_exec_bug
master_UBA
mixed
pobu168-patch-1
remove_node_sass
revert-2184-upgrade_work_orch
timed_execution
web_monitor_demo
workflow_report
2.1.0
v3.2.2
v3.2.1
v3.2.0
v3.1.0
v3.0.1
v3.0.0
v2.9.2
v2.9.1
v2.9.0
v2.8.1
v2.8.0
v2.7.1
v2.7.0
v2.6.1
v2.6.0
v2.5.0
v2.4.0
v2.3.1
v2.3.0
v2.2.0
v2.1.2
v2.1.1
No related merge requests found
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
platform-auth-server/src/main/java/com/webank/wecube/platform/auth/server/controller/AuthorityRoleRelationshipController.java
+39
-39
...erver/controller/AuthorityRoleRelationshipController.java
platform-auth-server/src/main/java/com/webank/wecube/platform/auth/server/service/AuthorityRoleRelationshipService.java
+25
-17
...auth/server/service/AuthorityRoleRelationshipService.java
platform-auth-server/src/main/java/com/webank/wecube/platform/auth/server/service/AuthorityService.java
+5
-0
...wecube/platform/auth/server/service/AuthorityService.java
platform-core/src/main/java/com/webank/wecube/platform/core/controller/RoleManagementController.java
+1
-1
...be/platform/core/controller/RoleManagementController.java
platform-core/src/main/java/com/webank/wecube/platform/core/controller/UserManagementController.java
+1
-1
...be/platform/core/controller/UserManagementController.java
platform-core/src/main/java/com/webank/wecube/platform/core/service/user/RoleMenuServiceImpl.java
+41
-20
...ecube/platform/core/service/user/RoleMenuServiceImpl.java
platform-core/src/test/java/com/webank/wecube/platform/core/service/user/RoleMenuServiceTest.java
+24
-21
...ecube/platform/core/service/user/RoleMenuServiceTest.java
with
136 additions
and
99 deletions
+136
-99
platform-auth-server/src/main/java/com/webank/wecube/platform/auth/server/controller/AuthorityRoleRelationshipController.java
+
39
-
39
View file @
b1dcc9f2
package
com.webank.wecube.platform.auth.server.controller
;
import
static
com
.
webank
.
wecube
.
platform
.
auth
.
server
.
dto
.
CommonResponseDto
.
okay
;
import
static
com
.
webank
.
wecube
.
platform
.
auth
.
server
.
dto
.
CommonResponseDto
.
okayWithData
;
import
java.util.List
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.DeleteMapping
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.PathVariable
;
import
org.springframework.web.bind.annotation.PostMapping
;
...
...
@@ -16,46 +20,42 @@ import com.webank.wecube.platform.auth.server.common.ApplicationConstants;
import
com.webank.wecube.platform.auth.server.dto.CommonResponseDto
;
import
com.webank.wecube.platform.auth.server.service.AuthorityRoleRelationshipService
;
import
static
com
.
webank
.
wecube
.
platform
.
auth
.
server
.
dto
.
CommonResponseDto
.
okayWithData
;
import
java.util.List
;
import
static
com
.
webank
.
wecube
.
platform
.
auth
.
server
.
dto
.
CommonResponseDto
.
okay
;
@RestController
@RequestMapping
(
ApplicationConstants
.
ApiInfo
.
PREFIX_DEFAULT
)
public
class
AuthorityRoleRelationshipController
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
AuthorityRoleRelationshipController
.
class
);
@Autowired
AuthorityRoleRelationshipService
authorityRoleRelationshipService
;
@GetMapping
(
"/authoritys/{authority-id}/roles"
)
@ResponseBody
public
CommonResponseDto
getRolesByAuthorityId
(
@PathVariable
(
value
=
"authority-id"
)
Long
authorityId
)
{
return
okayWithData
(
authorityRoleRelationshipService
.
getRolesByAuthorityId
(
authorityId
));
}
@GetMapping
(
"/roles/{role-id}/authoritys"
)
@ResponseBody
public
CommonResponseDto
getAuthoritysByRoleId
(
@PathVariable
(
value
=
"role-id"
)
String
roleId
)
{
return
okayWithData
(
authorityRoleRelationshipService
.
getAuthoritysByRoleId
(
roleId
));
}
@PostMapping
(
"/roles/{role-id}/authoritys"
)
@ResponseBody
public
CommonResponseDto
grantRoleForAuthoritys
(
@PathVariable
(
value
=
"role-id"
)
String
roleId
,
@RequestBody
List
<
Long
>
authorityIds
)
throws
Exception
{
authorityRoleRelationshipService
.
grantRoleForAuthoritys
(
roleId
,
authorityIds
);
return
okay
();
}
@DeleteMapping
(
"/roles/{role-id}/authoritys"
)
@ResponseBody
public
CommonResponseDto
revokeRoleForAuthoritys
(
@PathVariable
(
value
=
"role-id"
)
String
roleId
,
@RequestBody
List
<
Long
>
authorityIds
)
throws
Exception
{
authorityRoleRelationshipService
.
revokeRoleForAuthoritys
(
roleId
,
authorityIds
);
return
okay
();
}
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
AuthorityRoleRelationshipController
.
class
);
@Autowired
AuthorityRoleRelationshipService
authorityRoleRelationshipService
;
@GetMapping
(
"/authoritys/{authority-id}/roles"
)
@ResponseBody
public
CommonResponseDto
getRolesByAuthorityId
(
@PathVariable
(
value
=
"authority-id"
)
Long
authorityId
)
{
return
okayWithData
(
authorityRoleRelationshipService
.
getRolesByAuthorityId
(
authorityId
));
}
@GetMapping
(
"/roles/{role-id}/authorities"
)
@ResponseBody
public
CommonResponseDto
getAuthoritysByRoleId
(
@PathVariable
(
value
=
"role-id"
)
String
roleId
)
{
return
okayWithData
(
authorityRoleRelationshipService
.
getAuthoritysByRoleId
(
roleId
));
}
@PostMapping
(
"/roles/{role-id}/authorities/grant"
)
@ResponseBody
public
CommonResponseDto
grantRoleForAuthoritiesByCode
(
@PathVariable
(
value
=
"role-id"
)
String
roleId
,
@RequestBody
List
<
String
>
authorityCodes
)
throws
Exception
{
log
.
info
(
"grant authorities to role:roleId={},authorityCodes={}"
,
roleId
,
authorityCodes
);
authorityRoleRelationshipService
.
grantRoleForAuthoritiesByCode
(
roleId
,
authorityCodes
);
return
okay
();
}
@PostMapping
(
"/roles/{role-id}/authorities/revoke"
)
@ResponseBody
public
CommonResponseDto
revokeRoleForAuthoritiesByCode
(
@PathVariable
(
value
=
"role-id"
)
String
roleId
,
@RequestBody
List
<
String
>
authorityCodes
)
throws
Exception
{
log
.
info
(
"revoke authorities from role:roleId={},authorityCodes={}"
,
roleId
,
authorityCodes
);
authorityRoleRelationshipService
.
revokeRoleForAuthoritiesByCode
(
roleId
,
authorityCodes
);
return
okay
();
}
}
This diff is collapsed.
Click to expand it.
platform-auth-server/src/main/java/com/webank/wecube/platform/auth/server/service/AuthorityRoleRelationshipService.java
+
25
-
17
View file @
b1dcc9f2
...
...
@@ -8,18 +8,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Service
;
import
com.google.common.collect.Lists
;
import
com.webank.wecube.platform.auth.server.entity.ApiRoleRelationshipEntity
;
import
com.webank.wecube.platform.auth.server.entity.AuthorityRoleRelationshipEntity
;
import
com.webank.wecube.platform.auth.server.entity.SysApiEntity
;
import
com.webank.wecube.platform.auth.server.entity.SysAuthorityEntity
;
import
com.webank.wecube.platform.auth.server.entity.SysRoleEntity
;
import
com.webank.wecube.platform.auth.server.entity.SysUserEntity
;
import
com.webank.wecube.platform.auth.server.entity.UserRoleRelationshipEntity
;
import
com.webank.wecube.platform.auth.server.repository.ApiRepository
;
import
com.webank.wecube.platform.auth.server.repository.ApiRoleRelationshipRepository
;
import
com.webank.wecube.platform.auth.server.repository.AuthorityRepository
;
import
com.webank.wecube.platform.auth.server.repository.AuthorityRoleRelationshipRepository
;
import
com.webank.wecube.platform.auth.server.repository.UserRepository
;
import
com.webank.wecube.platform.auth.server.repository.UserRoleRelationshipRepository
;
@Service
(
"authorityRoleRelationshipService"
)
public
class
AuthorityRoleRelationshipService
{
...
...
@@ -34,6 +27,9 @@ public class AuthorityRoleRelationshipService {
@Autowired
private
AuthorityService
authorityService
;
@Autowired
private
AuthorityRepository
authorityRepository
;
public
List
<
SysAuthorityEntity
>
getAuthoritysByRoleId
(
String
roleId
)
{
List
<
SysAuthorityEntity
>
authoritys
=
Lists
.
newArrayList
();
authorityRoleRelationshipRepository
.
findByRoleId
(
roleId
).
forEach
(
authorityRole
->
{
...
...
@@ -50,23 +46,35 @@ public class AuthorityRoleRelationshipService {
return
roles
;
}
public
void
grantRoleForAuthorit
ys
(
String
roleId
,
List
<
Lo
ng
>
authority
Id
s
)
throws
Exception
{
public
void
grantRoleForAuthorit
iesByCode
(
String
roleId
,
List
<
Stri
ng
>
authority
Code
s
)
throws
Exception
{
SysRoleEntity
role
=
roleService
.
getRoleByIdIfExisted
(
roleId
);
for
(
Long
authorityId
:
authorityIds
)
{
SysAuthorityEntity
authorityEntity
=
authorityService
.
getAuthorityByIdIfExisted
(
authorityId
);
if
(
null
==
authorityRoleRelationshipRepository
.
findOneByAuthorityIdAndRoleId
(
authorityId
,
roleId
))
for
(
String
authorityCode
:
authorityCodes
)
{
SysAuthorityEntity
authorityEntity
=
authorityService
.
getAuthorityByCode
(
authorityCode
);
if
(
authorityEntity
==
null
)
{
SysAuthorityEntity
authority
=
new
SysAuthorityEntity
();
authority
.
setCode
(
authorityCode
);
authorityEntity
=
authorityRepository
.
save
(
authority
);
}
if
(
authorityRoleRelationshipRepository
.
findOneByAuthorityIdAndRoleId
(
authorityEntity
.
getId
(),
roleId
)
==
null
)
authorityRoleRelationshipRepository
.
save
(
new
AuthorityRoleRelationshipEntity
(
authorityEntity
,
role
));
}
}
public
void
revokeRoleForAuthorit
ys
(
String
roleId
,
List
<
Lo
ng
>
authority
Id
s
)
throws
Exception
{
public
void
revokeRoleForAuthorit
iesByCode
(
String
roleId
,
List
<
Stri
ng
>
authority
Code
s
)
throws
Exception
{
roleService
.
getRoleByIdIfExisted
(
roleId
);
for
(
Long
authorityId
:
authorityIds
)
{
authorityService
.
getAuthorityByIdIfExisted
(
authorityId
);
for
(
String
authorityCode
:
authorityCodes
)
{
SysAuthorityEntity
authorityEntity
=
authorityService
.
getAuthorityByCode
(
authorityCode
);
if
(
authorityEntity
==
null
)
{
continue
;
}
AuthorityRoleRelationshipEntity
authorityRoleRelationshipEntity
=
authorityRoleRelationshipRepository
.
findOneByAuthorityIdAndRoleId
(
authority
Id
,
roleId
);
if
(
null
!=
authorityRoleRelationshipEntity
)
.
findOneByAuthorityIdAndRoleId
(
authority
Entity
.
getId
()
,
roleId
);
if
(
authorityRoleRelationshipEntity
!=
null
)
{
authorityRoleRelationshipRepository
.
delete
(
authorityRoleRelationshipEntity
);
}
}
}
...
...
This diff is collapsed.
Click to expand it.
platform-auth-server/src/main/java/com/webank/wecube/platform/auth/server/service/AuthorityService.java
+
5
-
0
View file @
b1dcc9f2
...
...
@@ -44,6 +44,11 @@ public class AuthorityService {
public
void
delete
(
Long
id
)
{
authorityRepository
.
deleteById
(
id
);
}
public
SysAuthorityEntity
getAuthorityByCode
(
String
authrityCode
){
SysAuthorityEntity
entity
=
authorityRepository
.
findOneByCode
(
authrityCode
);
return
entity
;
}
public
SysAuthorityEntity
getAuthorityByIdIfExisted
(
Long
authorityId
)
throws
Exception
{
Optional
<
SysAuthorityEntity
>
authorityEntityOptional
=
authorityRepository
.
findById
(
authorityId
);
...
...
This diff is collapsed.
Click to expand it.
platform-core/src/main/java/com/webank/wecube/platform/core/controller/RoleManagementController.java
+
1
-
1
View file @
b1dcc9f2
...
...
@@ -14,7 +14,7 @@ import java.util.Map;
* @author howechen
*/
@RestController
@RequestMapping
(
"v1
/
"
)
@RequestMapping
(
"
/
v1"
)
public
class
RoleManagementController
{
private
UserManagementServiceImpl
userManagementService
;
...
...
This diff is collapsed.
Click to expand it.
platform-core/src/main/java/com/webank/wecube/platform/core/controller/UserManagementController.java
+
1
-
1
View file @
b1dcc9f2
...
...
@@ -14,7 +14,7 @@ import java.util.Map;
* @author howechen
*/
@RestController
@RequestMapping
(
"v1
/
"
)
@RequestMapping
(
"
/
v1"
)
public
class
UserManagementController
{
private
UserManagementServiceImpl
userManagementService
;
private
RoleMenuServiceImpl
roleMenuService
;
...
...
This diff is collapsed.
Click to expand it.
platform-core/src/main/java/com/webank/wecube/platform/core/service/user/RoleMenuServiceImpl.java
+
41
-
20
View file @
b1dcc9f2
package
com.webank.wecube.platform.core.service.user
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Optional
;
import
java.util.stream.Collectors
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
com.webank.wecube.platform.core.commons.ApplicationProperties
;
import
com.webank.wecube.platform.core.commons.WecubeCoreException
;
import
com.webank.wecube.platform.core.domain.MenuItem
;
import
com.webank.wecube.platform.core.domain.RoleMenu
;
...
...
@@ -7,20 +19,11 @@ import com.webank.wecube.platform.core.domain.plugin.PluginPackageMenu;
import
com.webank.wecube.platform.core.dto.MenuItemDto
;
import
com.webank.wecube.platform.core.dto.user.RoleDto
;
import
com.webank.wecube.platform.core.dto.user.RoleMenuDto
;
import
com.webank.wecube.platform.core.http.UserJwtSsoTokenRestTemplate
;
import
com.webank.wecube.platform.core.jpa.MenuItemRepository
;
import
com.webank.wecube.platform.core.jpa.PluginPackageMenuRepository
;
import
com.webank.wecube.platform.core.jpa.user.RoleMenuRepository
;
import
com.webank.wecube.platform.core.utils.JsonUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Optional
;
import
java.util.stream.Collectors
;
/**
* @author howechen
...
...
@@ -30,21 +33,21 @@ import java.util.stream.Collectors;
public
class
RoleMenuServiceImpl
implements
RoleMenuService
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
RoleMenuServiceImpl
.
class
);
@Autowired
private
RoleMenuRepository
roleMenuRepository
;
@Autowired
private
MenuItemRepository
menuItemRepository
;
@Autowired
private
PluginPackageMenuRepository
pluginPackageMenuRepository
;
@Autowired
private
UserManagementServiceImpl
userManagementService
;
@Autowired
public
RoleMenuServiceImpl
(
RoleMenuRepository
roleMenuRepository
,
MenuItemRepository
menuItemRepository
,
PluginPackageMenuRepository
pluginPackageMenuRepository
,
UserManagementServiceImpl
userManagementService
)
{
this
.
roleMenuRepository
=
roleMenuRepository
;
this
.
menuItemRepository
=
menuItemRepository
;
this
.
pluginPackageMenuRepository
=
pluginPackageMenuRepository
;
this
.
userManagementService
=
userManagementService
;
}
private
UserJwtSsoTokenRestTemplate
userJwtSsoTokenRestTemplate
;
@Autowired
private
ApplicationProperties
applicationProperties
;
/**
* Retrieve role_menu table by given roleId
...
...
@@ -106,8 +109,18 @@ public class RoleMenuServiceImpl implements RoleMenuService {
logger
.
info
(
String
.
format
(
"Deleting menus: [%s]"
,
needToDeleteList
));
for
(
RoleMenu
roleMenu
:
needToDeleteList
)
{
this
.
roleMenuRepository
.
deleteById
(
roleMenu
.
getId
());
}
}
List
<
String
>
menuCodesToRevoke
=
new
ArrayList
<>();
for
(
RoleMenu
rm
:
needToDeleteList
){
menuCodesToRevoke
.
add
(
"MENU_"
+
rm
.
getMenuCode
());
}
///roles/{role-id}/authorities/revoke
String
revokePath
=
String
.
format
(
"auth/roles/%s/authorities/revoke"
,
roleId
);
userJwtSsoTokenRestTemplate
.
postForObject
(
String
.
format
(
"http://%s/%s"
,
applicationProperties
.
getGatewayUrl
(),
revokePath
),
menuCodesToRevoke
,
String
.
class
);
// new menuCodeList - current menuCodeList = needToCreateList
List
<
String
>
needToCreateList
;
...
...
@@ -124,6 +137,14 @@ public class RoleMenuServiceImpl implements RoleMenuService {
logger
.
error
(
ex
.
getMessage
());
throw
new
WecubeCoreException
(
ex
.
getMessage
());
}
List
<
String
>
menuCodesToGrant
=
new
ArrayList
<>();
for
(
RoleMenu
rm
:
batchUpdateList
){
menuCodesToGrant
.
add
(
"MENU_"
+
rm
.
getMenuCode
());
}
String
grantPath
=
String
.
format
(
"auth/roles/%s/authorities/grant"
,
roleId
);
userJwtSsoTokenRestTemplate
.
postForObject
(
String
.
format
(
"http://%s/%s"
,
applicationProperties
.
getGatewayUrl
(),
grantPath
),
menuCodesToGrant
,
String
.
class
);
}
}
...
...
This diff is collapsed.
Click to expand it.
platform-core/src/test/java/com/webank/wecube/platform/core/service/user/RoleMenuServiceTest.java
+
24
-
21
View file @
b1dcc9f2
package
com.webank.wecube.platform.core.service.user
;
import
static
com
.
webank
.
wecube
.
platform
.
core
.
domain
.
plugin
.
PluginPackage
.
Status
.
RUNNING
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
springframework
.
test
.
web
.
client
.
match
.
MockRestRequestMatchers
.
method
;
import
static
org
.
springframework
.
test
.
web
.
client
.
match
.
MockRestRequestMatchers
.
requestTo
;
import
static
org
.
springframework
.
test
.
web
.
client
.
response
.
MockRestResponseCreators
.
withSuccess
;
import
java.sql.Timestamp
;
import
java.util.ArrayList
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Set
;
import
org.junit.Before
;
import
org.junit.Ignore
;
import
org.junit.Test
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Qualifier
;
import
org.springframework.http.HttpMethod
;
import
org.springframework.http.MediaType
;
import
org.springframework.test.web.client.ExpectedCount
;
import
org.springframework.test.web.client.MockRestServiceServer
;
import
org.springframework.web.client.RestTemplate
;
import
com.google.common.collect.Lists
;
import
com.webank.wecube.platform.core.DatabaseBasedTest
;
import
com.webank.wecube.platform.core.commons.ApplicationProperties
;
...
...
@@ -11,28 +34,8 @@ import com.webank.wecube.platform.core.dto.user.RoleMenuDto;
import
com.webank.wecube.platform.core.jpa.MenuItemRepository
;
import
com.webank.wecube.platform.core.jpa.PluginPackageRepository
;
import
com.webank.wecube.platform.core.jpa.user.RoleMenuRepository
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Qualifier
;
import
org.springframework.http.HttpMethod
;
import
org.springframework.http.MediaType
;
import
org.springframework.test.web.client.ExpectedCount
;
import
org.springframework.test.web.client.MockRestServiceServer
;
import
org.springframework.web.client.RestTemplate
;
import
java.sql.Timestamp
;
import
java.util.ArrayList
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Set
;
import
static
com
.
webank
.
wecube
.
platform
.
core
.
domain
.
plugin
.
PluginPackage
.
Status
.
RUNNING
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
springframework
.
test
.
web
.
client
.
match
.
MockRestRequestMatchers
.*;
import
static
org
.
springframework
.
test
.
web
.
client
.
response
.
MockRestResponseCreators
.
withSuccess
;
@Ignore
public
class
RoleMenuServiceTest
extends
DatabaseBasedTest
{
static
final
String
ROLE_ONE
=
"1"
;
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
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
Menu
Projects
Groups
Snippets
Help