-
Notifications
You must be signed in to change notification settings - Fork 4
Additional GCP data fetchers #119
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
Open
mjsprengers
wants to merge
31
commits into
Blazebit:main
Choose a base branch
from
mjsprengers:additional_gcp_fetchers
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
16b3312
feat: get signInActivity (only allowed if tenant has premium p1 or p2…
mjsprengers d4e5d3b
feat: add signInActivity to response if tenant has premium p1 or p2 l…
mjsprengers bb9af2d
feat: handle the case where Intune is not used / not licensed, return…
mjsprengers 71ab332
feat: add organization datafetcher
mjsprengers f9d5f61
Merge pull request #1 from mjsprengers/graphservice-datafetchers
mjsprengers 1ef3874
Revert "feat: add organization datafetcher"
mjsprengers 7f04c50
Revert "feat: handle the case where Intune is not used / not licensed…
mjsprengers 42cf40e
Revert "feat: add signInActivity to response if tenant has premium p1…
mjsprengers 94c616a
Revert "feat: get signInActivity (only allowed if tenant has premium …
mjsprengers fe66bff
Merge branch 'Blazebit:main' into main
mjsprengers 847c6f5
Merge branch 'Blazebit:main' into main
mjsprengers 80d4248
Merge branch 'Blazebit:main' into main
mjsprengers 8a2d48d
Merge branch 'Blazebit:main' into main
mjsprengers 61ab53b
Merge branch 'Blazebit:main' into main
mjsprengers 809f0a4
Merge branch 'Blazebit:main' into main
mjsprengers 8ff1310
Merge branch 'Blazebit:main' into main
mjsprengers ed13417
Merge branch 'Blazebit:main' into main
mjsprengers 4f1aaba
Merge branch 'Blazebit:main' into main
mjsprengers b578b8b
Merge branch 'Blazebit:main' into main
mjsprengers 6e3dbb4
Merge branch 'Blazebit:main' into main
mjsprengers 63982ac
Merge branch 'Blazebit:main' into main
mjsprengers d04a40a
Merge branch 'Blazebit:main' into main
mjsprengers b151ca5
Merge branch 'Blazebit:main' into main
mjsprengers 4d61d3b
Merge branch 'Blazebit:main' into main
mjsprengers 5e0c458
Merge branch 'Blazebit:main' into main
mjsprengers 5c7b9e3
Merge branch 'Blazebit:main' into main
mjsprengers 0dfb120
feat: add 5 new gcp datafetchers + tests
mjsprengers 64b3871
feat: add 5 new gcp datafetchers + tests
mjsprengers 3ec9917
fix build
mjsprengers 136a97f
include all fields in the object
mjsprengers d43300b
add gcp and endpoint verification to main for testing
mjsprengers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
80 changes: 80 additions & 0 deletions
80
...mpute/src/main/java/com/blazebit/query/connector/gcp/compute/FirewallRuleDataFetcher.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * Copyright Blazebit | ||
| */ | ||
| package com.blazebit.query.connector.gcp.compute; | ||
|
|
||
| import com.blazebit.query.connector.base.DataFormats; | ||
| import com.blazebit.query.connector.gcp.base.GcpConnectorConfig; | ||
| import com.blazebit.query.connector.gcp.base.GcpConventionContext; | ||
| import com.blazebit.query.connector.gcp.base.GcpProject; | ||
| import com.blazebit.query.spi.DataFetchContext; | ||
| import com.blazebit.query.spi.DataFetcher; | ||
| import com.blazebit.query.spi.DataFetcherException; | ||
| import com.blazebit.query.spi.DataFormat; | ||
| import com.google.api.client.http.HttpResponseException; | ||
| import com.google.api.gax.core.CredentialsProvider; | ||
| import com.google.api.gax.rpc.PermissionDeniedException; | ||
| import com.google.cloud.compute.v1.Firewall; | ||
| import com.google.cloud.compute.v1.FirewallsClient; | ||
| import com.google.cloud.compute.v1.FirewallsSettings; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.Serializable; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Fetches VPC Firewall rules for GCP projects. | ||
| * | ||
| * @author Blazebit | ||
| * @since 1.0.0 | ||
| */ | ||
| public class FirewallRuleDataFetcher implements DataFetcher<GcpFirewallRule>, Serializable { | ||
|
|
||
| public static final FirewallRuleDataFetcher INSTANCE = new FirewallRuleDataFetcher(); | ||
|
|
||
| private FirewallRuleDataFetcher() { | ||
| } | ||
|
|
||
| @Override | ||
| public List<GcpFirewallRule> fetch(DataFetchContext context) { | ||
| try { | ||
| List<CredentialsProvider> credentialsProviders = GcpConnectorConfig.GCP_CREDENTIALS_PROVIDER.getAll( context ); | ||
| List<GcpFirewallRule> list = new ArrayList<>(); | ||
| List<? extends GcpProject> projects = context.getSession().getOrFetch( GcpProject.class ); | ||
| for ( CredentialsProvider credentialsProvider : credentialsProviders ) { | ||
| final FirewallsSettings settings = FirewallsSettings.newBuilder() | ||
| .setCredentialsProvider( credentialsProvider ) | ||
| .build(); | ||
| try (FirewallsClient client = FirewallsClient.create( settings )) { | ||
| for ( GcpProject project : projects ) { | ||
| try { | ||
| for ( Firewall firewall : client.list( project.getPayload().getProjectId() ).iterateAll() ) { | ||
| list.add( new GcpFirewallRule( firewall.getName(), firewall ) ); | ||
| } | ||
| } | ||
| catch (PermissionDeniedException e) { | ||
| if ( e.getCause() instanceof HttpResponseException | ||
| && ((HttpResponseException) e.getCause()).getContent() | ||
| .contains( "\"accessNotConfigured\"" ) ) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this mean? Shouldn't we somehow surface this? |
||
| // Ignore this exception, since there are no resources | ||
| continue; | ||
| } | ||
| throw e; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return list; | ||
| } | ||
| catch (IOException e) { | ||
| throw new DataFetcherException( "Could not fetch firewall rule list", e ); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public DataFormat getDataFormat() { | ||
| return DataFormats.beansConvention( GcpFirewallRule.class, GcpConventionContext.INSTANCE ); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
...e/gcp/compute/src/main/java/com/blazebit/query/connector/gcp/compute/GcpFirewallRule.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * Copyright Blazebit | ||
| */ | ||
| package com.blazebit.query.connector.gcp.compute; | ||
|
|
||
| import com.blazebit.query.connector.gcp.base.GcpWrapper; | ||
| import com.google.cloud.compute.v1.Firewall; | ||
|
|
||
| /** | ||
| * Wrapper for a GCP VPC Firewall rule. | ||
| * | ||
| * @author Blazebit | ||
| * @since 1.0.0 | ||
| */ | ||
| public class GcpFirewallRule extends GcpWrapper<Firewall> { | ||
| public GcpFirewallRule(String resourceId, Firewall firewall) { | ||
| super( resourceId, firewall ); | ||
| } | ||
|
|
||
| @Override | ||
| public Firewall getPayload() { | ||
| return super.getPayload(); | ||
| } | ||
| } |
125 changes: 125 additions & 0 deletions
125
.../compute/src/test/java/com/blazebit/query/connector/gcp/compute/GcpFirewallRuleTests.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * Copyright Blazebit | ||
| */ | ||
| package com.blazebit.query.connector.gcp.compute; | ||
|
|
||
| import com.blazebit.query.QueryContext; | ||
| import com.blazebit.query.TypeReference; | ||
| import com.blazebit.query.impl.QueryContextBuilderImpl; | ||
| import com.google.cloud.compute.v1.Allowed; | ||
| import com.google.cloud.compute.v1.Firewall; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| public class GcpFirewallRuleTests { | ||
|
|
||
| private static final QueryContext CONTEXT; | ||
|
|
||
| static { | ||
| var builder = new QueryContextBuilderImpl(); | ||
| builder.registerSchemaProvider( new GcpComputeSchemaProvider() ); | ||
| builder.registerSchemaObjectAlias( GcpFirewallRule.class, "GcpFirewallRule" ); | ||
| CONTEXT = builder.build(); | ||
| } | ||
|
|
||
| private static GcpFirewallRule secureRule() { | ||
| Firewall firewall = Firewall.newBuilder() | ||
| .setName( "allow-internal" ) | ||
| .setDirection( "INGRESS" ) | ||
| .setDisabled( false ) | ||
| .setPriority( 1000 ) | ||
| .addSourceRanges( "10.0.0.0/8" ) | ||
| .addAllowed( Allowed.newBuilder().setIPProtocol( "tcp" ).addPorts( "443" ).build() ) | ||
| .build(); | ||
| return new GcpFirewallRule( "allow-internal", firewall ); | ||
| } | ||
|
|
||
| private static GcpFirewallRule sshOpenRule() { | ||
| Firewall firewall = Firewall.newBuilder() | ||
| .setName( "allow-ssh-world" ) | ||
| .setDirection( "INGRESS" ) | ||
| .setDisabled( false ) | ||
| .setPriority( 1000 ) | ||
| .addSourceRanges( "0.0.0.0/0" ) | ||
| .addAllowed( Allowed.newBuilder().setIPProtocol( "tcp" ).addPorts( "22" ).build() ) | ||
| .build(); | ||
| return new GcpFirewallRule( "allow-ssh-world", firewall ); | ||
| } | ||
|
|
||
| private static GcpFirewallRule rdpOpenRule() { | ||
| Firewall firewall = Firewall.newBuilder() | ||
| .setName( "allow-rdp-world" ) | ||
| .setDirection( "INGRESS" ) | ||
| .setDisabled( false ) | ||
| .setPriority( 1000 ) | ||
| .addSourceRanges( "0.0.0.0/0" ) | ||
| .addAllowed( Allowed.newBuilder().setIPProtocol( "tcp" ).addPorts( "3389" ).build() ) | ||
| .build(); | ||
| return new GcpFirewallRule( "allow-rdp-world", firewall ); | ||
| } | ||
|
|
||
| @Test | ||
| void should_return_all_firewall_rules() { | ||
| try (var session = CONTEXT.createSession()) { | ||
| session.put( GcpFirewallRule.class, List.of( secureRule(), sshOpenRule(), rdpOpenRule() ) ); | ||
|
|
||
| var result = session.createQuery( | ||
| "SELECT f.resourceId FROM GcpFirewallRule f", | ||
| new TypeReference<Map<String, Object>>() {} ).getResultList(); | ||
|
|
||
| assertThat( result ).hasSize( 3 ); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| void should_detect_unrestricted_ssh() { | ||
| try (var session = CONTEXT.createSession()) { | ||
| session.put( GcpFirewallRule.class, List.of( secureRule(), sshOpenRule() ) ); | ||
|
|
||
| // Detect ingress rules that allow port 22 from any source | ||
| var result = session.createQuery( | ||
| """ | ||
| SELECT f.resourceId, | ||
| f.payload.name, | ||
| f.payload.direction = 'INGRESS' | ||
| AND COALESCE(f.payload.disabled, false) = false AS passed | ||
| FROM GcpFirewallRule f | ||
| WHERE f.payload.direction = 'INGRESS' | ||
| """, | ||
| new TypeReference<Map<String, Object>>() {} ).getResultList(); | ||
|
|
||
| assertThat( result ).hasSize( 2 ); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| void should_detect_disabled_firewall_rules() { | ||
| try (var session = CONTEXT.createSession()) { | ||
| Firewall disabled = Firewall.newBuilder() | ||
| .setName( "disabled-rule" ) | ||
| .setDirection( "INGRESS" ) | ||
| .setDisabled( true ) | ||
| .build(); | ||
| GcpFirewallRule disabledRule = new GcpFirewallRule( "disabled-rule", disabled ); | ||
|
|
||
| session.put( GcpFirewallRule.class, List.of( secureRule(), disabledRule ) ); | ||
|
|
||
| var result = session.createQuery( | ||
| """ | ||
| SELECT f.resourceId, | ||
| COALESCE(f.payload.disabled, false) = false AS enabled | ||
| FROM GcpFirewallRule f | ||
| """, | ||
| new TypeReference<Map<String, Object>>() {} ).getResultList(); | ||
|
|
||
| assertThat( result ).hasSize( 2 ); | ||
| assertThat( result ).extracting( r -> r.get( "enabled" ) ) | ||
| .containsExactlyInAnyOrder( true, false ); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| plugins { | ||
| id 'blaze-query.java-conventions' | ||
| } | ||
|
|
||
| dependencies { | ||
| api project(':blaze-query-core-api') | ||
| api project(':blaze-query-connector-google-gcp-base') | ||
| api platform(libs.gcp.bom) | ||
| api libs.gcp.container | ||
| testImplementation project(':blaze-query-core-impl') | ||
| testImplementation libs.junit.jupiter | ||
| testImplementation libs.assertj.core | ||
| } | ||
|
|
||
| description = 'blaze-query-connector-google-gcp-container' |
30 changes: 30 additions & 0 deletions
30
.../src/main/java/com/blazebit/query/connector/gcp/container/GcpContainerSchemaProvider.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * Copyright Blazebit | ||
| */ | ||
| package com.blazebit.query.connector.gcp.container; | ||
|
|
||
| import com.blazebit.query.spi.ConfigurationProvider; | ||
| import com.blazebit.query.spi.DataFetcher; | ||
| import com.blazebit.query.spi.QuerySchemaProvider; | ||
|
|
||
| import java.util.Set; | ||
|
|
||
| /** | ||
| * The schema provider for the GCP Container (GKE) connector. | ||
| * | ||
| * @author Blazebit | ||
| * @since 1.0.0 | ||
| */ | ||
| public final class GcpContainerSchemaProvider implements QuerySchemaProvider { | ||
| /** | ||
| * Creates a new schema provider. | ||
| */ | ||
| public GcpContainerSchemaProvider() { | ||
| } | ||
|
|
||
| @Override | ||
| public Set<? extends DataFetcher<?>> resolveSchemaObjects(ConfigurationProvider configurationProvider) { | ||
| return Set.of( GkeClusterDataFetcher.INSTANCE ); | ||
| } | ||
| } |
25 changes: 25 additions & 0 deletions
25
...gcp/container/src/main/java/com/blazebit/query/connector/gcp/container/GcpGkeCluster.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * Copyright Blazebit | ||
| */ | ||
| package com.blazebit.query.connector.gcp.container; | ||
|
|
||
| import com.blazebit.query.connector.gcp.base.GcpWrapper; | ||
| import com.google.container.v1.Cluster; | ||
|
|
||
| /** | ||
| * Wrapper for a GCP GKE (Google Kubernetes Engine) cluster. | ||
| * | ||
| * @author Blazebit | ||
| * @since 1.0.0 | ||
| */ | ||
| public class GcpGkeCluster extends GcpWrapper<Cluster> { | ||
| public GcpGkeCluster(String resourceId, Cluster cluster) { | ||
| super( resourceId, cluster ); | ||
| } | ||
|
|
||
| @Override | ||
| public Cluster getPayload() { | ||
| return super.getPayload(); | ||
| } | ||
| } |
84 changes: 84 additions & 0 deletions
84
...ainer/src/main/java/com/blazebit/query/connector/gcp/container/GkeClusterDataFetcher.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * Copyright Blazebit | ||
| */ | ||
| package com.blazebit.query.connector.gcp.container; | ||
|
|
||
| import com.blazebit.query.connector.base.DataFormats; | ||
| import com.blazebit.query.connector.gcp.base.GcpConnectorConfig; | ||
| import com.blazebit.query.connector.gcp.base.GcpConventionContext; | ||
| import com.blazebit.query.connector.gcp.base.GcpProject; | ||
| import com.blazebit.query.spi.DataFetchContext; | ||
| import com.blazebit.query.spi.DataFetcher; | ||
| import com.blazebit.query.spi.DataFetcherException; | ||
| import com.blazebit.query.spi.DataFormat; | ||
| import com.google.api.client.http.HttpResponseException; | ||
| import com.google.api.gax.core.CredentialsProvider; | ||
| import com.google.api.gax.rpc.PermissionDeniedException; | ||
| import com.google.cloud.container.v1.ClusterManagerClient; | ||
| import com.google.cloud.container.v1.ClusterManagerSettings; | ||
| import com.google.container.v1.Cluster; | ||
| import com.google.container.v1.ListClustersRequest; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.Serializable; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Fetches GKE clusters for GCP projects. | ||
| * | ||
| * @author Blazebit | ||
| * @since 1.0.0 | ||
| */ | ||
| public class GkeClusterDataFetcher implements DataFetcher<GcpGkeCluster>, Serializable { | ||
|
|
||
| public static final GkeClusterDataFetcher INSTANCE = new GkeClusterDataFetcher(); | ||
|
|
||
| private GkeClusterDataFetcher() { | ||
| } | ||
|
|
||
| @Override | ||
| public List<GcpGkeCluster> fetch(DataFetchContext context) { | ||
| try { | ||
| List<CredentialsProvider> credentialsProviders = GcpConnectorConfig.GCP_CREDENTIALS_PROVIDER.getAll( context ); | ||
| List<GcpGkeCluster> list = new ArrayList<>(); | ||
| List<? extends GcpProject> projects = context.getSession().getOrFetch( GcpProject.class ); | ||
| for ( CredentialsProvider credentialsProvider : credentialsProviders ) { | ||
| final ClusterManagerSettings settings = ClusterManagerSettings.newBuilder() | ||
| .setCredentialsProvider( credentialsProvider ) | ||
| .build(); | ||
| try (ClusterManagerClient client = ClusterManagerClient.create( settings )) { | ||
| for ( GcpProject project : projects ) { | ||
| try { | ||
| ListClustersRequest request = ListClustersRequest.newBuilder() | ||
| .setParent( "projects/" + project.getPayload().getProjectId() + "/locations/-" ) | ||
| .build(); | ||
| for ( Cluster cluster : client.listClusters( request ).getClustersList() ) { | ||
| list.add( new GcpGkeCluster( cluster.getName(), cluster ) ); | ||
| } | ||
| } | ||
| catch (PermissionDeniedException e) { | ||
| if ( e.getCause() instanceof HttpResponseException | ||
| && ((HttpResponseException) e.getCause()).getContent() | ||
| .contains( "\"accessNotConfigured\"" ) ) { | ||
| // Ignore this exception, since there are no resources | ||
| continue; | ||
| } | ||
| throw e; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return list; | ||
| } | ||
| catch (IOException e) { | ||
| throw new DataFetcherException( "Could not fetch GKE cluster list", e ); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public DataFormat getDataFormat() { | ||
| return DataFormats.beansConvention( GcpGkeCluster.class, GcpConventionContext.INSTANCE ); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check
@author/@since