Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(open-api): get authorized apps #3647

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ecda570
feat(open-api): get authorized apps
Anilople Apr 24, 2021
29fa187
Merge branch 'master' into feature/openapi/enhancement/20210417
Anilople Apr 24, 2021
1c1522d
Merge branch 'master' into feature/openapi/enhancement/20210417
nobodyiam May 15, 2021
f0931c6
Merge branch 'master' into feature/openapi/enhancement/20210417
Anilople Jun 13, 2021
4d41625
refactor: delete new class
Anilople Jun 16, 2021
f3adccd
fix: test failure
Anilople Jun 16, 2021
472dd45
rename method name
Anilople Jun 16, 2021
5cacf99
Merge branch 'master' into feature/openapi/enhancement/20210417
Anilople Jun 16, 2021
73f4c01
Merge branch 'master' into feature/openapi/enhancement/20210417
Anilople Jun 16, 2021
c1d7cbd
fix: extractAppIdFromMasterRoleName -> extractAppIdFromRoleName
Anilople Jun 16, 2021
f9cdb84
test: find app authorized
Anilople Jun 16, 2021
1d45338
Merge branch 'master' into feature/openapi/enhancement/20210417
Anilople Jun 19, 2021
f4d182b
Update CHANGES.md
Anilople Jun 19, 2021
11d39b3
refactor: create method findAppIdsAuthorizedByConsumerId in ConsumerS…
Anilople Jun 20, 2021
1ddc81c
test: use sql to prepare data
Anilople Jun 20, 2021
348af40
test: add more app consumer-test-app-id-2
Anilople Jun 21, 2021
99822cb
test: restTemplate call api with token
Anilople Jun 21, 2021
dea5f3c
test: append 000 to all id in sql
Anilople Jun 25, 2021
c284ae5
test: move sql to folder /sql/openapi
Anilople Jun 25, 2021
5a999dc
Merge branch 'master' into feature/openapi/enhancement/20210417
nobodyiam Jun 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Apollo 1.9.0
* [add More... link for known users](https://github.com/ctripcorp/apollo/pull/3757)
* [update OIDC documentation](https://github.com/ctripcorp/apollo/pull/3766)
* [feature: add Spring Boot 2.4 config data loader support](https://github.com/ctripcorp/apollo/pull/3754)
* [feat(open-api): get authorized apps](https://github.com/ctripcorp/apollo/pull/3647)
------------------
All issues and pull requests are [here](https://github.com/ctripcorp/apollo/milestone/6?closed=1)

Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ public List<OpenAppDTO> getAllApps() {
return appService.getAppsInfo(null);
}

/**
* Get applications which can be operated by current open api client.
*
* @return app's information
*/
public List<OpenAppDTO> getAuthorizedApps() {
return this.appService.getAuthorizedApps();
}

/**
* Get App information by app ids
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,13 @@ public List<OpenAppDTO> getAppsInfo(List<String> appIds) {
throw new RuntimeException(String.format("Load app information for appIds: %s failed", appIds), ex);
}
}

public List<OpenAppDTO> getAuthorizedApps() {
String path = "apps/authorized";
try(CloseableHttpResponse response = this.get(path)) {
return gson.fromJson(EntityUtils.toString(response.getEntity()), OPEN_APP_DTO_LIST_TYPE);
} catch (Throwable ex) {
throw new RuntimeException("Load authorized apps failed", ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.ctrip.framework.apollo.portal.component.config.PortalConfig;
import com.ctrip.framework.apollo.portal.entity.bo.UserInfo;
import com.ctrip.framework.apollo.portal.entity.po.Role;
import com.ctrip.framework.apollo.portal.repository.RoleRepository;
import com.ctrip.framework.apollo.portal.service.RolePermissionService;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.portal.spi.UserService;
Expand All @@ -37,6 +38,9 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.hash.Hashing;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang.time.FastDateFormat;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -62,6 +66,7 @@ public class ConsumerService {
private final PortalConfig portalConfig;
private final RolePermissionService rolePermissionService;
private final UserService userService;
private final RoleRepository roleRepository;

public ConsumerService(
final UserInfoHolder userInfoHolder,
Expand All @@ -71,7 +76,8 @@ public ConsumerService(
final ConsumerRoleRepository consumerRoleRepository,
final PortalConfig portalConfig,
final RolePermissionService rolePermissionService,
final UserService userService) {
final UserService userService,
final RoleRepository roleRepository) {
this.userInfoHolder = userInfoHolder;
this.consumerTokenRepository = consumerTokenRepository;
this.consumerRepository = consumerRepository;
Expand All @@ -80,6 +86,7 @@ public ConsumerService(
this.portalConfig = portalConfig;
this.rolePermissionService = rolePermissionService;
this.userService = userService;
this.roleRepository = roleRepository;
}


Expand Down Expand Up @@ -257,4 +264,33 @@ ConsumerRole createConsumerRole(Long consumerId, Long roleId, String operator) {
return consumerRole;
}

public Set<String> findAppIdsAuthorizedByConsumerId(long consumerId) {
List<ConsumerRole> consumerRoles = this.findConsumerRolesByConsumerId(consumerId);
List<Long> roleIds = consumerRoles.stream().map(ConsumerRole::getRoleId)
.collect(Collectors.toList());

Set<String> appIds = this.findAppIdsByRoleIds(roleIds);
return appIds;
}

private List<ConsumerRole> findConsumerRolesByConsumerId(long consumerId) {
List<ConsumerRole> consumerRoles = this.consumerRoleRepository.findByConsumerId(consumerId);
return consumerRoles;
}

private Set<String> findAppIdsByRoleIds(List<Long> roleIds) {
Iterable<Role> roleIterable = this.roleRepository.findAllById(roleIds);

Set<String> appIds = new HashSet<>();

roleIterable.forEach(role -> {
if (!role.isDeleted()) {
String roleName = role.getRoleName();
String appId = RoleUtils.extractAppIdFromRoleName(roleName);
appIds.add(appId);
}
});

return appIds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import com.ctrip.framework.apollo.common.dto.ClusterDTO;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.openapi.entity.ConsumerRole;
import com.ctrip.framework.apollo.openapi.service.ConsumerService;
import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil;
import com.ctrip.framework.apollo.portal.environment.Env;
import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenEnvClusterDTO;
Expand All @@ -27,6 +30,8 @@
import com.ctrip.framework.apollo.portal.service.AppService;
import com.ctrip.framework.apollo.portal.service.ClusterService;
import com.google.common.collect.Sets;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

Expand All @@ -41,13 +46,19 @@ public class AppController {
private final PortalSettings portalSettings;
private final ClusterService clusterService;
private final AppService appService;
private final ConsumerAuthUtil consumerAuthUtil;
private final ConsumerService consumerService;

public AppController(final PortalSettings portalSettings,
final ClusterService clusterService,
final AppService appService) {
final ClusterService clusterService,
final AppService appService,
final ConsumerAuthUtil consumerAuthUtil,
final ConsumerService consumerService) {
this.portalSettings = portalSettings;
this.clusterService = clusterService;
this.appService = appService;
this.consumerAuthUtil = consumerAuthUtil;
this.consumerService = consumerService;
}

@GetMapping(value = "/apps/{appId}/envclusters")
Expand Down Expand Up @@ -80,4 +91,19 @@ public List<OpenAppDTO> findApps(@RequestParam(value = "appIds", required = fals
}
return OpenApiBeanUtils.transformFromApps(apps);
}

/**
* @return which apps can be operated by open api
*/
@GetMapping("/apps/authorized")
public List<OpenAppDTO> findAppsAuthorized(HttpServletRequest request) {
long consumerId = this.consumerAuthUtil.retrieveConsumerId(request);

Set<String> appIds = this.consumerService.findAppIdsAuthorizedByConsumerId(consumerId);

List<App> apps = this.appService.findByAppIds(appIds);
List<OpenAppDTO> openAppDTOS = OpenApiBeanUtils.transformFromApps(apps);
return openAppDTOS;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2021 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.openapi.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import com.ctrip.framework.apollo.portal.AbstractIntegrationTest;
import com.google.common.collect.Sets;
import java.util.Set;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;

/**
* @author wxq
*/
public class ConsumerServiceIntegrationTest extends AbstractIntegrationTest {

@Autowired
private ConsumerService consumerService;

@Test
@Sql(scripts = "/sql/openapi/ConsumerServiceIntegrationTest.testFindAppIdsAuthorizedByConsumerId.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testFindAppIdsAuthorizedByConsumerId() {
Set<String> appIds = this.consumerService.findAppIdsAuthorizedByConsumerId(1000L);
assertEquals(Sets.newHashSet("consumer-test-app-id-0", "consumer-test-app-id-1"), appIds);
assertFalse(appIds.contains("consumer-test-app-id-2"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2021 Apollo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.ctrip.framework.apollo.openapi.v1.controller;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import com.ctrip.framework.apollo.openapi.dto.OpenAppDTO;
import com.ctrip.framework.apollo.portal.AbstractIntegrationTest;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.jdbc.Sql;

/**
* Integration test for {@link AppController}.
*
* @author wxq
*/
public class AppControllerIntegrationTest extends AbstractIntegrationTest {

@Test
@Sql(scripts = "/sql/openapi/ConsumerServiceIntegrationTest.testFindAppIdsAuthorizedByConsumerId.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testFindAppsAuthorized() {
final String token = "3c16bf5b1f44b465179253442460e8c0ad845289";
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(HttpHeaders.AUTHORIZATION, token);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok to set token by this way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's ok

btw, is the following sql necessary? I think every test should clean its data after execution, so there is no need to clear the data before execution?

@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we delete the cleanup before run this test, code chagne to

  @Test
//  @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
  @Sql(scripts = "/com/ctrip/framework/apollo/openapi/service/ConsumerServiceIntegrationTest.testFindAppIdsAuthorizedByConsumerId.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
  @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
  public void testFindAppsAuthorized() {

and run a single test AppControllerIntegrationTest, will meet error org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.PERMISSION(ID)";

full log as follow

org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #12 of class path resource [com/ctrip/framework/apollo/openapi/service/ConsumerServiceIntegrationTest.testFindAppIdsAuthorizedByConsumerId.sql]: INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (1, 'AssignRole', 'consumer-test-app-id-0', 'apollo', 'apollo'); nested exception is org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.PERMISSION(ID)"; SQL statement:
INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (1, 'AssignRole', 'consumer-test-app-id-0', 'apollo', 'apollo') [23505-191]

	at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:622)
	at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254)
	at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:49)
	at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.execute(ResourceDatabasePopulator.java:269)
	at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.lambda$executeSqlScripts$4(SqlScriptsTestExecutionListener.java:278)
	at org.springframework.transaction.support.TransactionOperations.lambda$executeWithoutResult$0(TransactionOperations.java:68)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
	at org.springframework.transaction.support.TransactionOperations.executeWithoutResult(TransactionOperations.java:67)
	at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:278)
	at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.lambda$executeSqlScripts$0(SqlScriptsTestExecutionListener.java:200)
	at java.lang.Iterable.forEach(Iterable.java:75)
	at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:200)
	at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:144)
	at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.beforeTestMethod(SqlScriptsTestExecutionListener.java:117)
	at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:289)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.PERMISSION(ID)"; SQL statement:
INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (1, 'AssignRole', 'consumer-test-app-id-0', 'apollo', 'apollo') [23505-191]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
	at org.h2.message.DbException.get(DbException.java:179)
	at org.h2.message.DbException.get(DbException.java:155)
	at org.h2.mvstore.db.MVPrimaryIndex.add(MVPrimaryIndex.java:137)
	at org.h2.mvstore.db.MVTable.addRow(MVTable.java:704)
	at org.h2.command.dml.Insert.insertRows(Insert.java:156)
	at org.h2.command.dml.Insert.update(Insert.java:114)
	at org.h2.command.CommandContainer.update(CommandContainer.java:98)
	at org.h2.command.Command.executeUpdate(Command.java:258)
	at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:184)
	at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:158)
	at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95)
	at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java)
	at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:601)
	... 35 more

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, there is an initialization logic in com.ctrip.framework.apollo.portal.spi.defaultimpl.DefaultRoleInitializationService#initCreateAppRole which will create one set of role, permission and role permission record. I think we could change the id in the test sql.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, is it better to organize the sql file into the sql folder?

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have been moved to folder resources/sql/openapi


ResponseEntity<OpenAppDTO[]> responseEntity =
restTemplate.exchange(this.url("/openapi/v1/apps/authorized"), HttpMethod.GET,
new HttpEntity<>(httpHeaders), OpenAppDTO[].class);

OpenAppDTO[] openAppDTOS = responseEntity.getBody();
assertEquals(2, openAppDTOS.length);

Set<String> appIds = new HashSet<>();
for (OpenAppDTO openAppDTO : openAppDTOS) {
appIds.add(openAppDTO.getAppId());
}

assertTrue(appIds.contains("consumer-test-app-id-0"));
assertTrue(appIds.contains("consumer-test-app-id-1"));
}
}
Loading