Skip to content

Commit 7a6a316

Browse files
Provide in-memory implementation for OAuth2AuthorizationService
Fixes gh-43
1 parent e25e116 commit 7a6a316

File tree

4 files changed

+518
-0
lines changed

4 files changed

+518
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.server.authorization;
17+
18+
import org.springframework.util.Assert;
19+
20+
import java.util.Collections;
21+
import java.util.List;
22+
import java.util.concurrent.CopyOnWriteArrayList;
23+
24+
/**
25+
* In-memory implementation of {@link OAuth2AuthorizationService}.
26+
*
27+
* @author Krisztian Toth
28+
*/
29+
public final class InMemoryOAuth2AuthorizationService implements OAuth2AuthorizationService {
30+
private final List<OAuth2Authorization> authorizations;
31+
32+
/**
33+
* Creates an {@link InMemoryOAuth2AuthorizationService}.
34+
*/
35+
public InMemoryOAuth2AuthorizationService() {
36+
this(Collections.emptyList());
37+
}
38+
39+
/**
40+
* Creates an {@link InMemoryOAuth2AuthorizationService} with the provided {@link List}<{@link OAuth2Authorization}>
41+
* as the in-memory store.
42+
*
43+
* @param authorizations a {@link List}<{@link OAuth2Authorization}> object to use as the store
44+
*/
45+
public InMemoryOAuth2AuthorizationService(List<OAuth2Authorization> authorizations) {
46+
Assert.notNull(authorizations, "authorizations cannot be null");
47+
this.authorizations = new CopyOnWriteArrayList<>(authorizations);
48+
}
49+
50+
@Override
51+
public void save(OAuth2Authorization authorization) {
52+
Assert.notNull(authorization, "authorization cannot be null");
53+
this.authorizations.add(authorization);
54+
}
55+
56+
@Override
57+
public OAuth2Authorization findByTokenAndTokenType(String token, TokenType tokenType) {
58+
Assert.hasText(token, "token cannot be empty");
59+
Assert.notNull(tokenType, "tokenType cannot be null");
60+
return this.authorizations.stream()
61+
.filter(authorization -> doesMatch(authorization, token, tokenType))
62+
.findFirst()
63+
.orElse(null);
64+
65+
}
66+
67+
private boolean doesMatch(OAuth2Authorization authorization, String token, TokenType tokenType) {
68+
if (tokenType.equals(TokenType.ACCESS_TOKEN)) {
69+
return isAccessTokenEqual(token, authorization);
70+
} else if (tokenType.equals(TokenType.AUTHORIZATION_CODE)) {
71+
return isAuthorizationCodeEqual(token, authorization);
72+
}
73+
return false;
74+
}
75+
76+
private boolean isAccessTokenEqual(String token, OAuth2Authorization authorization) {
77+
return authorization.getAccessToken() != null && token.equals(authorization.getAccessToken().getTokenValue());
78+
}
79+
80+
private boolean isAuthorizationCodeEqual(String token, OAuth2Authorization authorization) {
81+
return token.equals(authorization.getAttributes().get(TokenType.AUTHORIZATION_CODE.getValue()));
82+
}
83+
}

core/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,196 @@
1616
package org.springframework.security.oauth2.server.authorization;
1717

1818
import org.springframework.security.oauth2.core.OAuth2AccessToken;
19+
import org.springframework.util.Assert;
1920

21+
import java.util.Collections;
22+
import java.util.HashMap;
2023
import java.util.Map;
24+
import java.util.Objects;
25+
import java.util.function.Consumer;
2126

2227
/**
28+
* Represents a collection of attributes which describe an OAuth 2.0 authorization context.
29+
*
2330
* @author Joe Grandja
31+
* @author Krisztian Toth
2432
*/
2533
public class OAuth2Authorization {
2634
private String registeredClientId;
2735
private String principalName;
2836
private OAuth2AccessToken accessToken;
2937
private Map<String, Object> attributes;
3038

39+
protected OAuth2Authorization() {
40+
}
41+
42+
public String getRegisteredClientId() {
43+
return this.registeredClientId;
44+
}
45+
46+
public String getPrincipalName() {
47+
return this.principalName;
48+
}
49+
50+
public OAuth2AccessToken getAccessToken() {
51+
return this.accessToken;
52+
}
53+
54+
public Map<String, Object> getAttributes() {
55+
return this.attributes;
56+
}
57+
58+
/**
59+
* Returns an attribute with the provided name or {@code null} if not found.
60+
*
61+
* @param name the name of the attribute
62+
* @param <T> the type of the attribute
63+
* @return the found attribute or {@code null}
64+
*/
65+
public <T> T getAttribute(String name) {
66+
Assert.hasText(name, "name cannot be empty");
67+
return (T) this.attributes.get(name);
68+
}
69+
70+
@Override
71+
public boolean equals(Object o) {
72+
if (this == o) {
73+
return true;
74+
}
75+
if (o == null || getClass() != o.getClass()) {
76+
return false;
77+
}
78+
OAuth2Authorization that = (OAuth2Authorization) o;
79+
return Objects.equals(this.registeredClientId, that.registeredClientId) &&
80+
Objects.equals(this.principalName, that.principalName) &&
81+
Objects.equals(this.accessToken, that.accessToken) &&
82+
Objects.equals(this.attributes, that.attributes);
83+
}
84+
85+
@Override
86+
public int hashCode() {
87+
return Objects.hash(this.registeredClientId, this.principalName, this.accessToken, this.attributes);
88+
}
89+
90+
/**
91+
* Returns an empty {@link Builder}.
92+
*
93+
* @return the {@link Builder}
94+
*/
95+
public static Builder builder() {
96+
return new Builder();
97+
}
98+
99+
/**
100+
* Returns a new {@link Builder}, initialized with the provided {@link OAuth2Authorization}.
101+
*
102+
* @param authorization the {@link OAuth2Authorization} to copy from
103+
* @return the {@link Builder}
104+
*/
105+
public static Builder withAuthorization(OAuth2Authorization authorization) {
106+
Assert.notNull(authorization, "authorization cannot be null");
107+
return new Builder(authorization);
108+
}
109+
110+
/**
111+
* Builder class for {@link OAuth2Authorization}.
112+
*/
113+
public static class Builder {
114+
private String registeredClientId;
115+
private String principalName;
116+
private OAuth2AccessToken accessToken;
117+
private Map<String, Object> attributes = new HashMap<>();
118+
119+
protected Builder() {
120+
}
121+
122+
protected Builder(OAuth2Authorization authorization) {
123+
this.registeredClientId = authorization.registeredClientId;
124+
this.principalName = authorization.principalName;
125+
this.accessToken = authorization.accessToken;
126+
this.attributes = authorization.attributes;
127+
}
128+
129+
/**
130+
* Sets the registered client identifier.
131+
*
132+
* @param registeredClientId the client id
133+
* @return the {@link Builder}
134+
*/
135+
public Builder registeredClientId(String registeredClientId) {
136+
this.registeredClientId = registeredClientId;
137+
return this;
138+
}
139+
140+
/**
141+
* Sets the principal name.
142+
*
143+
* @param principalName the principal name
144+
* @return the {@link Builder}
145+
*/
146+
public Builder principalName(String principalName) {
147+
this.principalName = principalName;
148+
return this;
149+
}
150+
151+
/**
152+
* Sets the {@link OAuth2AccessToken}.
153+
*
154+
* @param accessToken the {@link OAuth2AccessToken}
155+
* @return the {@link Builder}
156+
*/
157+
public Builder accessToken(OAuth2AccessToken accessToken) {
158+
this.accessToken = accessToken;
159+
return this;
160+
}
161+
162+
/**
163+
* Adds the attribute with the specified name and {@link String} value to the attributes map.
164+
*
165+
* @param name the name of the attribute
166+
* @param value the value of the attribute
167+
* @return the {@link Builder}
168+
*/
169+
public Builder attribute(String name, String value) {
170+
Assert.hasText(name, "name cannot be empty");
171+
Assert.hasText(value, "value cannot be empty");
172+
this.attributes.put(name, value);
173+
return this;
174+
}
175+
176+
/**
177+
* A {@code Consumer} of the attributes map allowing to access or modify its content.
178+
*
179+
* @param attributesConsumer a {@link Consumer} of the attributes map
180+
* @return the {@link Builder}
181+
*/
182+
public Builder attributes(Consumer<Map<String, Object>> attributesConsumer) {
183+
attributesConsumer.accept(this.attributes);
184+
return this;
185+
}
186+
187+
/**
188+
* Builds a new {@link OAuth2Authorization}.
189+
*
190+
* @return the {@link OAuth2Authorization}
191+
*/
192+
public OAuth2Authorization build() {
193+
Assert.hasText(this.registeredClientId, "registeredClientId cannot be empty");
194+
Assert.hasText(this.principalName, "principalName cannot be empty");
195+
if (this.accessToken == null && this.attributes.get(TokenType.AUTHORIZATION_CODE.getValue()) == null) {
196+
throw new IllegalArgumentException("either accessToken has to be set or the authorization code with key '"
197+
+ TokenType.AUTHORIZATION_CODE.getValue() + "' must be provided in the attributes map");
198+
}
199+
return create();
200+
}
201+
202+
private OAuth2Authorization create() {
203+
OAuth2Authorization oAuth2Authorization = new OAuth2Authorization();
204+
oAuth2Authorization.registeredClientId = this.registeredClientId;
205+
oAuth2Authorization.principalName = this.principalName;
206+
oAuth2Authorization.accessToken = this.accessToken;
207+
oAuth2Authorization.attributes = Collections.unmodifiableMap(this.attributes);
208+
return oAuth2Authorization;
209+
}
210+
}
31211
}

0 commit comments

Comments
 (0)