Skip to content

Commit

Permalink
Merge pull request #26 from wwan13/feature/resolve
Browse files Browse the repository at this point in the history
Resolvers
  • Loading branch information
wwan13 authored Jun 9, 2024
2 parents 74922c8 + 94e9a1f commit 15eed4d
Show file tree
Hide file tree
Showing 12 changed files with 893 additions and 0 deletions.
77 changes: 77 additions & 0 deletions src/main/java/io/wwan13/wintersecurity/resolve/RolesResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2024 the original author or 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 io.wwan13.wintersecurity.resolve;

import io.wwan13.wintersecurity.jwt.PayloadAnalysis;
import io.wwan13.wintersecurity.jwt.TokenClaims;
import io.wwan13.wintersecurity.resolve.util.AttributeExtractor;
import io.wwan13.wintersecurity.util.TypeConverter;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;

public class RolesResolver implements HandlerMethodArgumentResolver {

private final TargetAnnotations targetAnnotations;
private final PayloadAnalysis payloadAnalysis;

public RolesResolver(
TargetAnnotations targetAnnotations,
PayloadAnalysis payloadAnalysis
) {
this.targetAnnotations = targetAnnotations;
this.payloadAnalysis = payloadAnalysis;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasAnnotation = targetAnnotations.forRoles().stream()
.anyMatch(parameter::hasParameterAnnotation);
boolean isValidType = payloadAnalysis.roles().getType()
.isAssignableFrom(parameter.getParameterType());

return hasAnnotation && isValidType;
}

@Override
public Object resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory
) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();

TokenClaims claims = AttributeExtractor.extractClaims(request);

boolean isCollection = Collection.class.isAssignableFrom(parameter.getParameterType());
if (!isCollection) {
String role = claims.getRoles().stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Empty role entered"));

return TypeConverter.convertTo(role, parameter.getParameterType());
}

return TypeConverter.convertTo(claims.getRoles(), parameter.getParameterType());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2024 the original author or 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 io.wwan13.wintersecurity.resolve;

import io.wwan13.wintersecurity.jwt.PayloadAnalysis;
import io.wwan13.wintersecurity.jwt.TokenClaims;
import io.wwan13.wintersecurity.resolve.util.AttributeExtractor;
import io.wwan13.wintersecurity.util.TypeConverter;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;

public class SubjectResolver implements HandlerMethodArgumentResolver {

private final TargetAnnotations targetAnnotations;
private final PayloadAnalysis payloadAnalysis;

public SubjectResolver(
TargetAnnotations targetAnnotations,
PayloadAnalysis payloadAnalysis
) {
this.targetAnnotations = targetAnnotations;
this.payloadAnalysis = payloadAnalysis;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasAnnotation = targetAnnotations.forSubject().stream()
.anyMatch(parameter::hasParameterAnnotation);
boolean isValidType = payloadAnalysis.subject().getType()
.isAssignableFrom(parameter.getParameterType());

return hasAnnotation && isValidType;
}

@Override
public Object resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory
) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
TokenClaims claims = AttributeExtractor.extractClaims(request);

return TypeConverter.convertTo(
claims.getSubject(),
parameter.getParameterType()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2024 the original author or 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 io.wwan13.wintersecurity.resolve.util;

import io.wwan13.wintersecurity.constant.Constants;
import io.wwan13.wintersecurity.jwt.TokenClaims;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

public class AttributeExtractor {

private AttributeExtractor() {
throw new IllegalStateException("Cannot instantiate a utility class!");
}

public static Object extract(
HttpServletRequest request,
String attributeKey
) {
return extractAttributeWithNullChecking(request, attributeKey);
}

public static TokenClaims extractClaims(HttpServletRequest request) {
return (TokenClaims) extractAttributeWithNullChecking(request, Constants.ATTRIBUTE_CLAIMS_KEY);
}

private static Object extractAttributeWithNullChecking(
HttpServletRequest request,
String attributeKey
) {
Object claims = request.getAttribute(attributeKey);
if (Objects.isNull(claims)) {
throw new IllegalStateException("cannot extract claims");
}
return claims;
}
}
34 changes: 34 additions & 0 deletions src/main/java/io/wwan13/wintersecurity/util/TypeConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2024 the original author or 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 io.wwan13.wintersecurity.util;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

public class TypeConverter {

private static final ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

private TypeConverter() {
throw new IllegalStateException("Cannot instantiate a utility class!");
}

public static Object convertTo(Object originValue, Class<?> targetClazz) {
return objectMapper.convertValue(originValue, targetClazz);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ public void getRequestUriWillReturn(String uri) {
this.uri = uri;
}

public StubHttpServletRequest(Map<String, Object> attribute) {
this.attribute = attribute;
}

public StubHttpServletRequest() {
}

@Override
public String getHeader(String name) {
return expectedHeader;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2024 the original author or 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 io.wwan13.wintersecurity.resolve;

import io.wwan13.wintersecurity.jwt.PayloadAnalysis;
import io.wwan13.wintersecurity.jwt.PayloadAnalyst;
import io.wwan13.wintersecurity.jwt.TokenClaims;
import io.wwan13.wintersecurity.jwt.payload.annotation.Roles;
import io.wwan13.wintersecurity.jwt.payload.annotation.Subject;
import io.wwan13.wintersecurity.jwt.payload.support.DefaultPayloadAnalyst;

import java.util.Map;
import java.util.Set;

public class ResolveTestContainer {

public static TargetAnnotations targetAnnotations = new TargetAnnotations(
Set.of(RequestUserSubject.class, RequestUserId.class),
Set.of(RequestUserRoles.class)
);

public static PayloadAnalysis payloadAnalysis;

static {
PayloadAnalyst payloadAnalyst = new DefaultPayloadAnalyst();
payloadAnalysis = payloadAnalyst.analyze(ResolveTestPayload.class);
}

public static TokenClaims defaultTestClaims = new TokenClaims(
Map.of(
"sub", "1",
"roles", "ROLE_USER"
)
);

public static class ResolveTestPayload {
@Subject
Long subject;
@Roles
Set<String> roles;
}

public static class ResolveTestPayloadWithStringRole {
@Subject
Long subject;
@Roles
String role;
}
}
Loading

0 comments on commit 15eed4d

Please sign in to comment.