From 61278f5fd8622ed9594cd021c96d2403e0161f94 Mon Sep 17 00:00:00 2001 From: Mevan Date: Thu, 17 Oct 2024 16:18:18 +0530 Subject: [PATCH] Add temporary API Key authenticator impl --- adapter/config/default_config.go | 3 +- adapter/config/types.go | 3 +- adapter/internal/discovery/xds/marshaller.go | 3 +- .../config/enforcer/api_key_config.pb.go | 38 +- .../config/enforcer/api_key_config.proto | 1 + .../config/enforcer/APIKeyConfig.java | 138 ++++++ .../enforcer/APIKeyConfigOrBuilder.java | 12 + .../config/enforcer/APIKeyConfigProto.java | 14 +- .../connect/enforcer/config/ConfigHolder.java | 1 + .../enforcer/config/dto/APIKeyDTO.java | 9 + .../security/jwt/APIKeyAuthenticator.java | 427 ++---------------- .../security/jwt/APIKeyConstants.java | 2 + .../enforcer/security/jwt/APIKeyUtils.java | 48 +- 13 files changed, 278 insertions(+), 421 deletions(-) diff --git a/adapter/config/default_config.go b/adapter/config/default_config.go index 75e57c01b7..c72370914b 100644 --- a/adapter/config/default_config.go +++ b/adapter/config/default_config.go @@ -228,7 +228,8 @@ var defaultConfig = &Config{ DropConsoleTestHeaders: true, }, APIKeyConfig: apiKeyConfig{ - OAuthAgentURL: "https://localhost:9443", + InternalAPIKeyHeader: "Choreo-API-Key", + OAuthAgentURL: "https://localhost:9443", }, PATConfig: patConfig{ TokenExpiryTimeSkew: 1, diff --git a/adapter/config/types.go b/adapter/config/types.go index 7ebabe44b1..f6969328d0 100644 --- a/adapter/config/types.go +++ b/adapter/config/types.go @@ -500,7 +500,8 @@ type authHeader struct { } type apiKeyConfig struct { - OAuthAgentURL string `toml:"oauthAgentURL"` + InternalAPIKeyHeader string `toml:"internalAPIKeyHeader"` + OAuthAgentURL string `toml:"oauthAgentURL"` } type patConfig struct { diff --git a/adapter/internal/discovery/xds/marshaller.go b/adapter/internal/discovery/xds/marshaller.go index 73d676a81f..9cfaf65147 100644 --- a/adapter/internal/discovery/xds/marshaller.go +++ b/adapter/internal/discovery/xds/marshaller.go @@ -219,7 +219,8 @@ func MarshalConfig(config *config.Config) *enforcer.Config { DropConsoleTestHeaders: config.Enforcer.Security.AuthHeader.DropConsoleTestHeaders, }, ApiKeyConfig: &enforcer.APIKeyConfig{ - OauthAgentURL: config.Enforcer.Security.APIKeyConfig.OAuthAgentURL, + InternalAPIKeyHeader: config.Enforcer.Security.APIKeyConfig.InternalAPIKeyHeader, + OauthAgentURL: config.Enforcer.Security.APIKeyConfig.OAuthAgentURL, }, PatConfig: &enforcer.PATConfig{ TokenExpiryTimeSkew: config.Enforcer.Security.PATConfig.TokenExpiryTimeSkew, diff --git a/adapter/pkg/discovery/api/wso2/discovery/config/enforcer/api_key_config.pb.go b/adapter/pkg/discovery/api/wso2/discovery/config/enforcer/api_key_config.pb.go index c701a7b285..926f99c7a5 100644 --- a/adapter/pkg/discovery/api/wso2/discovery/config/enforcer/api_key_config.pb.go +++ b/adapter/pkg/discovery/api/wso2/discovery/config/enforcer/api_key_config.pb.go @@ -25,7 +25,8 @@ type APIKeyConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OauthAgentURL string `protobuf:"bytes,1,opt,name=oauthAgentURL,proto3" json:"oauthAgentURL,omitempty"` + OauthAgentURL string `protobuf:"bytes,1,opt,name=oauthAgentURL,proto3" json:"oauthAgentURL,omitempty"` + InternalAPIKeyHeader string `protobuf:"bytes,2,opt,name=internalAPIKeyHeader,proto3" json:"internalAPIKeyHeader,omitempty"` } func (x *APIKeyConfig) Reset() { @@ -67,6 +68,13 @@ func (x *APIKeyConfig) GetOauthAgentURL() string { return "" } +func (x *APIKeyConfig) GetInternalAPIKeyHeader() string { + if x != nil { + return x.InternalAPIKeyHeader + } + return "" +} + var File_wso2_discovery_config_enforcer_api_key_config_proto protoreflect.FileDescriptor var file_wso2_discovery_config_enforcer_api_key_config_proto_rawDesc = []byte{ @@ -75,20 +83,24 @@ var file_wso2_discovery_config_enforcer_api_key_config_proto_rawDesc = []byte{ 0x2f, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1e, 0x77, 0x73, 0x6f, 0x32, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x65, 0x6e, 0x66, - 0x6f, 0x72, 0x63, 0x65, 0x72, 0x22, 0x34, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x43, + 0x6f, 0x72, 0x63, 0x65, 0x72, 0x22, 0x68, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x61, - 0x75, 0x74, 0x68, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x55, 0x52, 0x4c, 0x42, 0x98, 0x01, 0x0a, 0x31, - 0x6f, 0x72, 0x67, 0x2e, 0x77, 0x73, 0x6f, 0x32, 0x2e, 0x63, 0x68, 0x6f, 0x72, 0x65, 0x6f, 0x2e, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, - 0x72, 0x42, 0x11, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x67, 0x6f, - 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x77, - 0x73, 0x6f, 0x32, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x72, 0x3b, 0x65, 0x6e, - 0x66, 0x6f, 0x72, 0x63, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x74, 0x68, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x32, 0x0a, 0x14, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, + 0x98, 0x01, 0x0a, 0x31, 0x6f, 0x72, 0x67, 0x2e, 0x77, 0x73, 0x6f, 0x32, 0x2e, 0x63, 0x68, 0x6f, + 0x72, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x65, 0x6e, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x72, 0x42, 0x11, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2f, 0x67, 0x6f, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2d, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2f, 0x77, 0x73, 0x6f, 0x32, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x72, 0x3b, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/api/proto/wso2/discovery/config/enforcer/api_key_config.proto b/api/proto/wso2/discovery/config/enforcer/api_key_config.proto index 5c1eb80496..be64efdc12 100644 --- a/api/proto/wso2/discovery/config/enforcer/api_key_config.proto +++ b/api/proto/wso2/discovery/config/enforcer/api_key_config.proto @@ -9,4 +9,5 @@ option java_multiple_files = true; message APIKeyConfig { string oauthAgentURL = 1; + string internalAPIKeyHeader = 2; } diff --git a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfig.java b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfig.java index d151a47f65..0305de475f 100644 --- a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfig.java +++ b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfig.java @@ -17,6 +17,7 @@ private APIKeyConfig(com.google.protobuf.GeneratedMessageV3.Builder builder) } private APIKeyConfig() { oauthAgentURL_ = ""; + internalAPIKeyHeader_ = ""; } @java.lang.Override @@ -55,6 +56,12 @@ private APIKeyConfig( oauthAgentURL_ = s; break; } + case 18: { + java.lang.String s = input.readStringRequireUtf8(); + + internalAPIKeyHeader_ = s; + break; + } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { @@ -125,6 +132,44 @@ public java.lang.String getOauthAgentURL() { } } + public static final int INTERNALAPIKEYHEADER_FIELD_NUMBER = 2; + private volatile java.lang.Object internalAPIKeyHeader_; + /** + * string internalAPIKeyHeader = 2; + * @return The internalAPIKeyHeader. + */ + @java.lang.Override + public java.lang.String getInternalAPIKeyHeader() { + java.lang.Object ref = internalAPIKeyHeader_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + internalAPIKeyHeader_ = s; + return s; + } + } + /** + * string internalAPIKeyHeader = 2; + * @return The bytes for internalAPIKeyHeader. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getInternalAPIKeyHeaderBytes() { + java.lang.Object ref = internalAPIKeyHeader_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + internalAPIKeyHeader_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -142,6 +187,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!getOauthAgentURLBytes().isEmpty()) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, oauthAgentURL_); } + if (!getInternalAPIKeyHeaderBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, internalAPIKeyHeader_); + } unknownFields.writeTo(output); } @@ -154,6 +202,9 @@ public int getSerializedSize() { if (!getOauthAgentURLBytes().isEmpty()) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, oauthAgentURL_); } + if (!getInternalAPIKeyHeaderBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, internalAPIKeyHeader_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -171,6 +222,8 @@ public boolean equals(final java.lang.Object obj) { if (!getOauthAgentURL() .equals(other.getOauthAgentURL())) return false; + if (!getInternalAPIKeyHeader() + .equals(other.getInternalAPIKeyHeader())) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -184,6 +237,8 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + OAUTHAGENTURL_FIELD_NUMBER; hash = (53 * hash) + getOauthAgentURL().hashCode(); + hash = (37 * hash) + INTERNALAPIKEYHEADER_FIELD_NUMBER; + hash = (53 * hash) + getInternalAPIKeyHeader().hashCode(); hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -319,6 +374,8 @@ public Builder clear() { super.clear(); oauthAgentURL_ = ""; + internalAPIKeyHeader_ = ""; + return this; } @@ -346,6 +403,7 @@ public org.wso2.choreo.connect.discovery.config.enforcer.APIKeyConfig build() { public org.wso2.choreo.connect.discovery.config.enforcer.APIKeyConfig buildPartial() { org.wso2.choreo.connect.discovery.config.enforcer.APIKeyConfig result = new org.wso2.choreo.connect.discovery.config.enforcer.APIKeyConfig(this); result.oauthAgentURL_ = oauthAgentURL_; + result.internalAPIKeyHeader_ = internalAPIKeyHeader_; onBuilt(); return result; } @@ -398,6 +456,10 @@ public Builder mergeFrom(org.wso2.choreo.connect.discovery.config.enforcer.APIKe oauthAgentURL_ = other.oauthAgentURL_; onChanged(); } + if (!other.getInternalAPIKeyHeader().isEmpty()) { + internalAPIKeyHeader_ = other.internalAPIKeyHeader_; + onChanged(); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -502,6 +564,82 @@ public Builder setOauthAgentURLBytes( onChanged(); return this; } + + private java.lang.Object internalAPIKeyHeader_ = ""; + /** + * string internalAPIKeyHeader = 2; + * @return The internalAPIKeyHeader. + */ + public java.lang.String getInternalAPIKeyHeader() { + java.lang.Object ref = internalAPIKeyHeader_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + internalAPIKeyHeader_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string internalAPIKeyHeader = 2; + * @return The bytes for internalAPIKeyHeader. + */ + public com.google.protobuf.ByteString + getInternalAPIKeyHeaderBytes() { + java.lang.Object ref = internalAPIKeyHeader_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + internalAPIKeyHeader_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string internalAPIKeyHeader = 2; + * @param value The internalAPIKeyHeader to set. + * @return This builder for chaining. + */ + public Builder setInternalAPIKeyHeader( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + internalAPIKeyHeader_ = value; + onChanged(); + return this; + } + /** + * string internalAPIKeyHeader = 2; + * @return This builder for chaining. + */ + public Builder clearInternalAPIKeyHeader() { + + internalAPIKeyHeader_ = getDefaultInstance().getInternalAPIKeyHeader(); + onChanged(); + return this; + } + /** + * string internalAPIKeyHeader = 2; + * @param value The bytes for internalAPIKeyHeader to set. + * @return This builder for chaining. + */ + public Builder setInternalAPIKeyHeaderBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + internalAPIKeyHeader_ = value; + onChanged(); + return this; + } @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { diff --git a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigOrBuilder.java b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigOrBuilder.java index 6a44e7b39c..18f20034b8 100644 --- a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigOrBuilder.java +++ b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigOrBuilder.java @@ -18,4 +18,16 @@ public interface APIKeyConfigOrBuilder extends */ com.google.protobuf.ByteString getOauthAgentURLBytes(); + + /** + * string internalAPIKeyHeader = 2; + * @return The internalAPIKeyHeader. + */ + java.lang.String getInternalAPIKeyHeader(); + /** + * string internalAPIKeyHeader = 2; + * @return The bytes for internalAPIKeyHeader. + */ + com.google.protobuf.ByteString + getInternalAPIKeyHeaderBytes(); } diff --git a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigProto.java b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigProto.java index fce84732e5..d9638eaa47 100644 --- a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigProto.java +++ b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigProto.java @@ -30,12 +30,12 @@ public static void registerAllExtensions( java.lang.String[] descriptorData = { "\n3wso2/discovery/config/enforcer/api_key" + "_config.proto\022\036wso2.discovery.config.enf" + - "orcer\"%\n\014APIKeyConfig\022\025\n\roauthAgentURL\030\001" + - " \001(\tB\230\001\n1org.wso2.choreo.connect.discove" + - "ry.config.enforcerB\021APIKeyConfigProtoP\001Z" + - "Ngithub.com/envoyproxy/go-control-plane/" + - "wso2/discovery/config/enforcer;enforcerb" + - "\006proto3" + "orcer\"C\n\014APIKeyConfig\022\025\n\roauthAgentURL\030\001" + + " \001(\t\022\034\n\024internalAPIKeyHeader\030\002 \001(\tB\230\001\n1o" + + "rg.wso2.choreo.connect.discovery.config." + + "enforcerB\021APIKeyConfigProtoP\001ZNgithub.co" + + "m/envoyproxy/go-control-plane/wso2/disco" + + "very/config/enforcer;enforcerb\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -46,7 +46,7 @@ public static void registerAllExtensions( internal_static_wso2_discovery_config_enforcer_APIKeyConfig_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_wso2_discovery_config_enforcer_APIKeyConfig_descriptor, - new java.lang.String[] { "OauthAgentURL", }); + new java.lang.String[] { "OauthAgentURL", "InternalAPIKeyHeader", }); } // @@protoc_insertion_point(outer_class_scope) diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/ConfigHolder.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/ConfigHolder.java index 1f23e3da71..148bf9a3f6 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/ConfigHolder.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/ConfigHolder.java @@ -224,6 +224,7 @@ private void populateAuthHeaderConfigurations(AuthHeader authHeader) { private void populateAPIKeyConfigs(APIKeyConfig apiKeyConfig) { APIKeyDTO apiKeyDTO = new APIKeyDTO(); + apiKeyDTO.setApiKeyInternalHeader(apiKeyConfig.getInternalAPIKeyHeader()); apiKeyDTO.setOauthAgentURL(apiKeyConfig.getOauthAgentURL()); config.setApiKeyConfig(apiKeyDTO); } diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/dto/APIKeyDTO.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/dto/APIKeyDTO.java index 759e544628..1dab51cb5f 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/dto/APIKeyDTO.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/dto/APIKeyDTO.java @@ -23,8 +23,17 @@ */ public class APIKeyDTO { + private String apiKeyInternalHeader; private String oauthAgentURL; + public String getApiKeyInternalHeader() { + return apiKeyInternalHeader; + } + + public void setApiKeyInternalHeader(String apiKeyInternalHeader) { + this.apiKeyInternalHeader = apiKeyInternalHeader; + } + public String getOauthAgentURL() { return oauthAgentURL; } diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyAuthenticator.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyAuthenticator.java index 0ce61ffcde..d55b0203a4 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyAuthenticator.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyAuthenticator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -18,430 +18,71 @@ package org.wso2.choreo.connect.enforcer.security.jwt; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; -import org.apache.commons.lang3.StringUtils; +import net.minidev.json.JSONValue; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.wso2.carbon.apimgt.common.gateway.dto.JWTConfigurationDto; -import org.wso2.carbon.apimgt.common.gateway.dto.JWTInfoDto; -import org.wso2.carbon.apimgt.common.gateway.dto.JWTValidationInfo; -import org.wso2.carbon.apimgt.common.gateway.jwtgenerator.AbstractAPIMgtGatewayJWTGenerator; -import org.wso2.choreo.connect.enforcer.common.CacheProvider; import org.wso2.choreo.connect.enforcer.commons.model.AuthenticationContext; import org.wso2.choreo.connect.enforcer.commons.model.RequestContext; -import org.wso2.choreo.connect.enforcer.commons.model.SecuritySchemaConfig; import org.wso2.choreo.connect.enforcer.config.ConfigHolder; -import org.wso2.choreo.connect.enforcer.config.EnforcerConfig; -import org.wso2.choreo.connect.enforcer.config.dto.ExtendedTokenIssuerDto; import org.wso2.choreo.connect.enforcer.constants.APIConstants; -import org.wso2.choreo.connect.enforcer.constants.APISecurityConstants; -import org.wso2.choreo.connect.enforcer.constants.GeneralErrorCodeConstants; -import org.wso2.choreo.connect.enforcer.dto.APIKeyValidationInfoDTO; -import org.wso2.choreo.connect.enforcer.dto.JWTTokenPayloadInfo; import org.wso2.choreo.connect.enforcer.exception.APISecurityException; -import org.wso2.choreo.connect.enforcer.keymgt.KeyManagerHolder; -import org.wso2.choreo.connect.enforcer.security.KeyValidator; -import org.wso2.choreo.connect.enforcer.util.BackendJwtUtils; -import org.wso2.choreo.connect.enforcer.util.FilterUtils; -import org.wso2.choreo.connect.enforcer.util.JWTUtils; +import org.wso2.choreo.connect.enforcer.security.jwt.validator.JWTConstants; -import java.math.BigInteger; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.text.ParseException; +import java.util.Base64; import java.util.Map; /** - * Extends the APIKeyHandler to authenticate request using API Key. + * API Key authenticator. */ -public class APIKeyAuthenticator extends APIKeyHandler { +public class APIKeyAuthenticator extends JWTAuthenticator { private static final Logger log = LogManager.getLogger(APIKeyAuthenticator.class); - private AbstractAPIMgtGatewayJWTGenerator jwtGenerator; - private final boolean isGatewayTokenCacheEnabled; - private boolean apiKeySubValidationEnabled = false; - private static final int IPV4_ADDRESS_BIT_LENGTH = 32; - private static final int IPV6_ADDRESS_BIT_LENGTH = 128; + private static boolean isAPIKeyEnabled = false; + + static { + if (System.getenv("API_KEY_ENABLED") != null) { + isAPIKeyEnabled = Boolean.parseBoolean(System.getenv("API_KEY_ENABLED")); + } + } public APIKeyAuthenticator() { + super(); log.debug("API key authenticator initialized."); - EnforcerConfig enforcerConfig = ConfigHolder.getInstance().getConfig(); - this.isGatewayTokenCacheEnabled = enforcerConfig.getCacheDto().isEnabled(); - if (enforcerConfig.getJwtConfigurationDto().isEnabled()) { - this.jwtGenerator = BackendJwtUtils.getApiMgtGatewayJWTGenerator(); - } - Map tokenIssuers = KeyManagerHolder.getInstance().getTokenIssuerMap() - .get(APIConstants.SUPER_TENANT_DOMAIN_NAME); - for (ExtendedTokenIssuerDto tokenIssuer: tokenIssuers.values()) { - if (APIConstants.KeyManager.APIM_PUBLISHER_ISSUER.equals(tokenIssuer.getName())) { - apiKeySubValidationEnabled = tokenIssuer.isValidateSubscriptions(); - break; - } - } } @Override public boolean canAuthenticate(RequestContext requestContext) { - return isAPIKey(getAPIKeyFromRequest(requestContext)); - } - // Gets API key from request - private static String getAPIKeyFromRequest(RequestContext requestContext) { - Map securitySchemaDefinitions = requestContext.getMatchedAPI(). - getSecuritySchemeDefinitions(); - // loop over resource security and get definition for the matching security definition name - for (String securityDefinitionName : requestContext.getMatchedResourcePath() - .getSecuritySchemas().keySet()) { - if (securitySchemaDefinitions.containsKey(securityDefinitionName)) { - SecuritySchemaConfig securitySchemaDefinition = - securitySchemaDefinitions.get(securityDefinitionName); - if (APIConstants.SWAGGER_API_KEY_AUTH_TYPE_NAME.equalsIgnoreCase( - securitySchemaDefinition.getType())) { - // If Defined in openAPI definition (when not enabled at APIM App level), - // key must exist in specified location - if (APIConstants.SWAGGER_API_KEY_IN_HEADER.equalsIgnoreCase(securitySchemaDefinition.getIn())) { - if (requestContext.getHeaders().containsKey(securitySchemaDefinition.getName())) { - return requestContext.getHeaders().get(securitySchemaDefinition.getName()); - } - } - if (APIConstants.SWAGGER_API_KEY_IN_QUERY.equalsIgnoreCase(securitySchemaDefinition.getIn())) { - if (requestContext.getQueryParameters().containsKey(securitySchemaDefinition.getName())) { - return requestContext.getQueryParameters().get(securitySchemaDefinition.getName()); - } - } - } - } + if (!isAPIKeyEnabled) { + return false; } - return ""; + String apiKeyValue = getAPIKeyFromRequest(requestContext); + return apiKeyValue != null && apiKeyValue.startsWith(APIKeyConstants.API_KEY_PREFIX); } @Override public AuthenticationContext authenticate(RequestContext requestContext) throws APISecurityException { - if (requestContext.getMatchedAPI() == null) { - log.debug("API Key Authentication failed"); - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), - APISecurityConstants.API_AUTH_GENERAL_ERROR, - APISecurityConstants.API_AUTH_GENERAL_ERROR_MESSAGE); - } - try { - String apiKey = getAPIKeyFromRequest(requestContext); - String[] splitToken = apiKey.split("\\."); - - SignedJWT signedJWT = SignedJWT.parse(apiKey); - JWSHeader jwsHeader = signedJWT.getHeader(); - JWTClaimsSet payload = signedJWT.getJWTClaimsSet(); - - String apiVersion = requestContext.getMatchedAPI().getVersion(); - String apiContext = requestContext.getMatchedAPI().getBasePath(); - String apiUuid = requestContext.getMatchedAPI().getUuid(); - - // Avoids using internal API keys, when internal key header or queryParam configured as api_key - if (isInternalKey(payload)) { - log.error("Invalid API Key token type. {} ", FilterUtils.getMaskedToken(splitToken[0])); - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), - APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, - APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE); - } - - // Gives jti (also used to populate authentication context) - String tokenIdentifier = payload.getJWTID(); - - // Checks whether key contains in revoked map. - checkInRevokedMap(tokenIdentifier, splitToken); - - // Verifies the token if it is found in cache - JWTTokenPayloadInfo jwtTokenPayloadInfo = (JWTTokenPayloadInfo) - CacheProvider.getGatewayAPIKeyDataCache().getIfPresent(tokenIdentifier); - boolean isVerified = isVerifiedApiKeyInCache(tokenIdentifier, apiKey, payload, splitToken, - "API Key", jwtTokenPayloadInfo); - - // Verifies token when it is not found in cache - if (!isVerified) { - isVerified = verifyTokenWhenNotInCache(jwsHeader, signedJWT, splitToken, payload, "API Key"); - } - - if (isVerified) { - log.debug("API Key signature is verified."); - if (jwtTokenPayloadInfo == null) { - log.debug("API Key payload not found in the cache."); - - jwtTokenPayloadInfo = new JWTTokenPayloadInfo(); - jwtTokenPayloadInfo.setPayload(payload); - jwtTokenPayloadInfo.setAccessToken(apiKey); - CacheProvider.getGatewayAPIKeyDataCache().put(tokenIdentifier, jwtTokenPayloadInfo); - } - - validateAPIKeyRestrictions(payload, requestContext, apiContext, apiVersion); - APIKeyValidationInfoDTO validationInfoDto; - if (apiKeySubValidationEnabled) { - validationInfoDto = KeyValidator.validateSubscription(requestContext.getMatchedAPI(), payload); - } else { - validationInfoDto = getAPIKeyValidationDTO(requestContext, payload); - } - - if (!validationInfoDto.isAuthorized()) { - if (GeneralErrorCodeConstants.API_BLOCKED_CODE == validationInfoDto - .getValidationStatus()) { - requestContext.getProperties().put(APIConstants.MessageFormat.ERROR_MESSAGE, - GeneralErrorCodeConstants.API_BLOCKED_MESSAGE); - requestContext.getProperties().put(APIConstants.MessageFormat.ERROR_DESCRIPTION, - GeneralErrorCodeConstants.API_BLOCKED_DESCRIPTION); - throw new APISecurityException(APIConstants.StatusCodes.SERVICE_UNAVAILABLE - .getCode(), validationInfoDto.getValidationStatus(), - GeneralErrorCodeConstants.API_BLOCKED_MESSAGE); - } - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHORIZED.getCode(), - validationInfoDto.getValidationStatus(), - "User is NOT authorized to access the Resource. " - + "API Subscription validation failed."); - } - - log.debug("API Key authentication successful."); - - // TODO: Add analytics data processing - - // Get SignedJWTInfo - SignedJWTInfo signedJWTInfo = JWTUtils.getSignedJwt(apiKey); - - // Get JWTValidationInfo - JWTValidationInfo validationInfo = new JWTValidationInfo(); - validationInfo.setUser(payload.getSubject()); - - // Generate or get backend JWT - String endUserToken = null; - JWTConfigurationDto jwtConfigurationDto = ConfigHolder.getInstance(). - getConfig().getJwtConfigurationDto(); - if (jwtConfigurationDto.isEnabled() && requestContext.getMatchedAPI().isEnableBackendJWT()) { - JWTInfoDto jwtInfoDto = FilterUtils - .generateJWTInfoDto(null, validationInfo, validationInfoDto, requestContext); - endUserToken = BackendJwtUtils.generateAndRetrieveJWTToken(jwtGenerator, tokenIdentifier, - jwtInfoDto, isGatewayTokenCacheEnabled); - // Set generated jwt token as a response header - requestContext.addOrModifyHeaders(jwtConfigurationDto.getJwtHeader(), endUserToken); - } - - // Create authentication context - JWTClaimsSet claims = signedJWTInfo.getJwtClaimsSet(); - AuthenticationContext authenticationContext = FilterUtils - .generateAuthenticationContext(requestContext, tokenIdentifier, validationInfo, - validationInfoDto, endUserToken, apiKey, false); - if (claims.getClaim("keytype") != null) { - authenticationContext.setKeyType(claims.getClaim("keytype").toString()); - } - log.debug("Analytics data processing for API Key (jti) {} was successful", tokenIdentifier); - return authenticationContext; - - } - } catch (ParseException e) { - log.warn("API Key authentication failed. ", e); - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), - APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, - "API key authentication failed."); - } - log.warn("API Key authentication failed."); - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHENTICATED.getCode(), - APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, - "API key authentication failed."); + String apiKeyHeaderValue = getAPIKeyFromRequest(requestContext); + // Skipping the prefix(`chk_`) and checksum. + String apiKeyData = apiKeyHeaderValue.substring(4, apiKeyHeaderValue.length() - 6); + // Base 64 decode key data. + String decodedKeyData = new String(Base64.getDecoder().decode(apiKeyData)); + // Convert data into JSON. + JSONObject jsonObject = (JSONObject) JSONValue.parse(decodedKeyData); + // Extracting the jwt token. + String jwtToken = jsonObject.getAsString(APIKeyConstants.API_KEY_JSON_KEY); + // Add the JWT as the Authorization header to authenticate the request. + requestContext.getHeaders().put(APIConstants.AUTHORIZATION_HEADER_DEFAULT, + JWTConstants.BEARER + " " + jwtToken); + return super.authenticate(requestContext); } - private APIKeyValidationInfoDTO getAPIKeyValidationDTO(RequestContext requestContext, JWTClaimsSet payload) - throws ParseException, APISecurityException { - - APIKeyValidationInfoDTO validationInfoDTO = new APIKeyValidationInfoDTO(); - JSONObject app = payload.getJSONObjectClaim(APIConstants.JwtTokenConstants.APPLICATION); - JSONObject api = null; - - if (payload.getClaim(APIConstants.JwtTokenConstants.KEY_TYPE) != null) { - validationInfoDTO.setType(payload.getStringClaim(APIConstants.JwtTokenConstants.KEY_TYPE)); - } else { - validationInfoDTO.setType(APIConstants.API_KEY_TYPE_PRODUCTION); - } - if (app != null) { - validationInfoDTO.setApplicationId(app.getAsNumber(APIConstants.JwtTokenConstants.APPLICATION_ID) - .intValue()); - validationInfoDTO.setApplicationUUID(app.getAsString(APIConstants.JwtTokenConstants.APPLICATION_UUID)); - validationInfoDTO.setApplicationName(app.getAsString(APIConstants.JwtTokenConstants.APPLICATION_NAME)); - validationInfoDTO.setApplicationTier(app.getAsString(APIConstants.JwtTokenConstants.APPLICATION_TIER)); - validationInfoDTO.setSubscriber(app.getAsString(APIConstants.JwtTokenConstants.APPLICATION_OWNER)); - } - - //check whether name is assigned correctly (This was not populated in JWTAuthenticator) - String name = requestContext.getMatchedAPI().getName(); - String version = requestContext.getMatchedAPI().getVersion(); - validationInfoDTO.setApiName(name); - validationInfoDTO.setApiVersion(version); - - if (payload.getClaim(APIConstants.JwtTokenConstants.SUBSCRIBED_APIS) != null) { - // Subscription validation - JSONArray subscribedAPIs = - (JSONArray) payload.getClaim(APIConstants.JwtTokenConstants.SUBSCRIBED_APIS); - for (Object apiObj : subscribedAPIs) { - JSONObject subApi = - (JSONObject) apiObj; - if (name.equals(subApi.getAsString(APIConstants.JwtTokenConstants.API_NAME)) && - version.equals(subApi.getAsString(APIConstants.JwtTokenConstants.API_VERSION) - )) { - api = subApi; - validationInfoDTO.setAuthorized(true); - - //set throttling attributes if present - String subTier = subApi.getAsString(APIConstants.JwtTokenConstants.SUBSCRIPTION_TIER); - String subPublisher = subApi.getAsString(APIConstants.JwtTokenConstants.API_PUBLISHER); - String subTenant = subApi.getAsString(APIConstants.JwtTokenConstants.SUBSCRIBER_TENANT_DOMAIN); - if (subTier != null) { - validationInfoDTO.setTier(subTier); - AuthenticatorUtils.populateTierInfo(validationInfoDTO, payload, subTier); - } - if (subPublisher != null) { - validationInfoDTO.setApiPublisher(subPublisher); - } - if (subTenant != null) { - validationInfoDTO.setSubscriberTenantDomain(subTenant); - } - - log.debug("APIKeyValidationInfoDTO populated for API: {}, version: {}.", name, version); - - break; - } - } - if (api == null) { - log.debug("Subscription data not populated in APIKeyValidationInfoDTO for the API: {}, version: {}.", - name, version); - log.error("User's subscription details cannot obtain for the API : {}", name); - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHORIZED.getCode(), - APISecurityConstants.API_AUTH_FORBIDDEN, - APISecurityConstants.API_AUTH_FORBIDDEN_MESSAGE); - } - } - return validationInfoDTO; - } - - private void validateAPIKeyRestrictions(JWTClaimsSet payload, RequestContext requestContext, String apiContext, - String apiVersion) throws APISecurityException { - String permittedIPList = null; - if (payload.getClaim(APIConstants.JwtTokenConstants.PERMITTED_IP) != null) { - permittedIPList = (String) payload.getClaim(APIConstants.JwtTokenConstants.PERMITTED_IP); - } - - if (StringUtils.isNotEmpty(permittedIPList)) { - // Validate client IP against permitted IPs - String clientIP = requestContext.getClientIp(); - - if (StringUtils.isNotEmpty(clientIP)) { - for (String restrictedIP : permittedIPList.split(",")) { - if (isIpInNetwork(clientIP, restrictedIP.trim())) { - // Client IP is allowed - return; - } - } - if (StringUtils.isNotEmpty(clientIP)) { - log.debug("Invocations to API: {}:{} is not permitted for client with IP: {}", - apiContext, apiVersion, clientIP); - } - - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHORIZED.getCode(), - APISecurityConstants.API_AUTH_FORBIDDEN, APISecurityConstants.API_AUTH_FORBIDDEN_MESSAGE); - } - - } - - String permittedRefererList = null; - if (payload.getClaim(APIConstants.JwtTokenConstants.PERMITTED_REFERER) != null) { - permittedRefererList = (String) payload.getClaim(APIConstants.JwtTokenConstants.PERMITTED_REFERER); - } - if (StringUtils.isNotEmpty(permittedRefererList)) { - // Validate http referer against the permitted referrers - Map transportHeaderMap = requestContext.getHeaders(); - if (transportHeaderMap != null) { - String referer = transportHeaderMap.get("referer"); - if (StringUtils.isNotEmpty(referer)) { - for (String restrictedReferer : permittedRefererList.split(",")) { - String restrictedRefererRegExp = restrictedReferer.trim() - .replace("*", "[^ ]*"); - if (referer.matches(restrictedRefererRegExp)) { - // Referer is allowed - return; - } - } - if (StringUtils.isNotEmpty(referer)) { - log.debug("Invocations to API: {}:{} is not permitted for referer: {}", - apiContext, apiVersion, referer); - } - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHORIZED.getCode(), - APISecurityConstants.API_AUTH_FORBIDDEN, APISecurityConstants.API_AUTH_FORBIDDEN_MESSAGE); - } else { - throw new APISecurityException(APIConstants.StatusCodes.UNAUTHORIZED.getCode(), - APISecurityConstants.API_AUTH_FORBIDDEN, APISecurityConstants.API_AUTH_FORBIDDEN_MESSAGE); - } - } - } - } - - private boolean isIpInNetwork(String ip, String cidr) { - - if (StringUtils.isEmpty(ip) || StringUtils.isEmpty(cidr)) { - return false; - } - ip = ip.trim(); - cidr = cidr.trim(); - - if (cidr.contains("/")) { - String[] cidrArr = cidr.split("/"); - if (cidrArr.length < 2 || (ip.contains(".") && !cidr.contains(".")) || - (ip.contains(":") && !cidr.contains(":"))) { - return false; - } - - BigInteger netAddress = ipToBigInteger(cidrArr[0]); - int netBits = Integer.parseInt(cidrArr[1]); - BigInteger givenIP = ipToBigInteger(ip); - - if (ip.contains(".")) { - // IPv4 - if (netAddress.shiftRight(IPV4_ADDRESS_BIT_LENGTH - netBits) - .shiftLeft(IPV4_ADDRESS_BIT_LENGTH - netBits).compareTo( - givenIP.shiftRight(IPV4_ADDRESS_BIT_LENGTH - netBits) - .shiftLeft(IPV4_ADDRESS_BIT_LENGTH - netBits)) == 0) { - return true; - } - } else if (ip.contains(":")) { - // IPv6 - if (netAddress.shiftRight(IPV6_ADDRESS_BIT_LENGTH - netBits) - .shiftLeft(IPV6_ADDRESS_BIT_LENGTH - netBits).compareTo( - givenIP.shiftRight(IPV6_ADDRESS_BIT_LENGTH - netBits) - .shiftLeft(IPV6_ADDRESS_BIT_LENGTH - netBits)) == 0) { - return true; - } - } - } else if (ip.equals(cidr)) { - return true; - } - return false; - } - - private BigInteger ipToBigInteger(String ipAddress) { - - InetAddress address; - try { - address = getAddress(ipAddress); - byte[] bytes = address.getAddress(); - return new BigInteger(1, bytes); - } catch (UnknownHostException e) { - //ignore the error and log it - log.error("Error while parsing host IP {}", ipAddress, e); - } - return BigInteger.ZERO; - } - - private InetAddress getAddress(String ipAddress) throws UnknownHostException { - - return InetAddress.getByName(ipAddress); + private String getAPIKeyFromRequest(RequestContext requestContext) { + Map headers = requestContext.getHeaders(); + return headers.get(ConfigHolder.getInstance().getConfig().getApiKeyConfig().getApiKeyInternalHeader()); } @Override diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyConstants.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyConstants.java index 5345377ed1..8d64a8b110 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyConstants.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyConstants.java @@ -24,8 +24,10 @@ public class APIKeyConstants { public static final String PAT_PREFIX = "chp_"; + public static final String API_KEY_PREFIX = "chk_"; public static final String API_KEY_JSON_KEY = "key"; public static final String PAT_EXCHANGE_ENDPOINT = "/auth/pat"; + public static final String API_KEY_EXCHANGE_ENDPOINT = "/auth/apiKey/token"; } diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyUtils.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyUtils.java index 55e1c021ca..a861fe162a 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyUtils.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyUtils.java @@ -95,12 +95,12 @@ public static String generateAPIKeyHash(String apiKey) { } /** - * This function exchanges a given API key to an JWT token. + * This function exchanges a given PAT hash to an JWT token. * - * @param keyHash Key Hash + * @param patHash Key Hash * @return JWT corresponding to given PAT. */ - public static Optional exchangePATToJWT(String keyHash) { + public static Optional exchangePATToJWT(String patHash) { URL url = null; try { @@ -115,7 +115,45 @@ public static Optional exchangePATToJWT(String keyHash) { // Create a request to exchange API key to JWT. HttpPost exchangeRequest = new HttpPost(url.toURI()); exchangeRequest.addHeader("Content-Type", ContentType.APPLICATION_JSON.toString()); - exchangeRequest.setEntity(new StringEntity(createPATExchangeRequest(keyHash))); + exchangeRequest.setEntity(new StringEntity(createKeyHashExchangeRequest(patHash))); + try (CloseableHttpResponse response = httpClient.execute(exchangeRequest)) { + if (response.getStatusLine().getStatusCode() == 200) { + HttpEntity entity = response.getEntity(); + try (InputStream content = entity.getContent()) { + OAuthAgentResponse resp = gson.fromJson(IOUtils.toString(content), + OAuthAgentResponse.class); + return Optional.of(resp.getAccessToken()); + } + } + } + } catch (Exception e) { + log.error("Error occurred while exchanging API key to JWT", e); + } + return Optional.empty(); + } + + /** + * Exchange a given API key hash to a JWT token. + * + * @param apiKeyHash API Key Hash + * @return JWT corresponding to given API Key. + */ + public static Optional exchangeAPIKeyToJWT(String apiKeyHash) { + + URL url = null; + try { + String apiKeyExchangeURL = String.format("%s%s", ConfigHolder.getInstance().getConfig() + .getApiKeyConfig().getOauthAgentURL(), APIKeyConstants.API_KEY_EXCHANGE_ENDPOINT); + url = new URL(apiKeyExchangeURL); + } catch (MalformedURLException e) { + log.error("Error occurred while parsing OAuth agent URL", e); + return Optional.empty(); + } + try (CloseableHttpClient httpClient = (CloseableHttpClient) FilterUtils.getHttpClient(url.getProtocol())) { + // Create a request to exchange API key to JWT. + HttpPost exchangeRequest = new HttpPost(url.toURI()); + exchangeRequest.addHeader("Content-Type", ContentType.APPLICATION_JSON.toString()); + exchangeRequest.setEntity(new StringEntity(createKeyHashExchangeRequest(apiKeyHash))); try (CloseableHttpResponse response = httpClient.execute(exchangeRequest)) { if (response.getStatusLine().getStatusCode() == 200) { HttpEntity entity = response.getEntity(); @@ -161,7 +199,7 @@ private static String generateCRC32Checksum(String data) { return Base64.getEncoder().withoutPadding().encodeToString(checksumBytes); } - private static String createPATExchangeRequest(String keyHash) { + private static String createKeyHashExchangeRequest(String keyHash) { Map patRequest = new HashMap<>(); patRequest.put("apiKeyHash", keyHash); return gson.toJson(patRequest);