1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.logic;
20
21 import com.fasterxml.jackson.databind.JsonNode;
22 import com.fasterxml.jackson.databind.json.JsonMapper;
23 import com.fasterxml.jackson.databind.node.ArrayNode;
24 import java.io.IOException;
25 import java.lang.reflect.Method;
26 import java.net.URI;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import javax.ws.rs.core.UriBuilder;
31 import org.apache.syncope.common.lib.scim.SCIMConf;
32 import org.apache.syncope.common.lib.to.EntityTO;
33 import org.apache.syncope.core.logic.scim.SCIMConfManager;
34 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
35 import org.apache.syncope.ext.scimv2.api.data.AuthenticationScheme;
36 import org.apache.syncope.ext.scimv2.api.data.BulkConfigurationOption;
37 import org.apache.syncope.ext.scimv2.api.data.ConfigurationOption;
38 import org.apache.syncope.ext.scimv2.api.data.FilterConfigurationOption;
39 import org.apache.syncope.ext.scimv2.api.data.Meta;
40 import org.apache.syncope.ext.scimv2.api.data.ResourceType;
41 import org.apache.syncope.ext.scimv2.api.data.SchemaExtension;
42 import org.apache.syncope.ext.scimv2.api.data.ServiceProviderConfig;
43 import org.apache.syncope.ext.scimv2.api.type.Resource;
44 import org.springframework.security.access.prepost.PreAuthorize;
45
46 public class SCIMLogic extends AbstractLogic<EntityTO> {
47
48 protected static final String SCHEMAS_JSON = "schemas.json";
49
50 protected static final Object MONITOR = new Object();
51
52 protected static ServiceProviderConfig SERVICE_PROVIDER_CONFIG;
53
54 protected static ResourceType USER;
55
56 protected static ResourceType GROUP;
57
58 protected static String SCHEMAS;
59
60 protected static final Map<String, String> SCHEMA_MAP = new HashMap<>();
61
62 protected final SCIMConfManager confManager;
63
64 public SCIMLogic(final SCIMConfManager confManager) {
65 this.confManager = confManager;
66 }
67
68 protected void init() {
69 try {
70 JsonMapper mapper = JsonMapper.builder().findAndAddModules().build();
71 JsonNode tree = mapper.readTree(SCIMLogic.class.getResourceAsStream('/' + SCHEMAS_JSON));
72 if (!tree.isArray()) {
73 throw new IOException("JSON node is not a tree");
74 }
75
76 ArrayNode schemaArray = (ArrayNode) tree;
77 SCHEMAS = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(tree);
78
79 for (JsonNode schema : schemaArray) {
80 SCHEMA_MAP.put(schema.get("id").asText(), mapper.writeValueAsString(schema));
81 }
82 } catch (IOException e) {
83 LOG.error("Could not parse the default schema definitions", e);
84 }
85 }
86
87 @PreAuthorize("isAuthenticated()")
88 public ServiceProviderConfig serviceProviderConfig(final UriBuilder uriBuilder) {
89 synchronized (MONITOR) {
90 if (SCHEMAS == null) {
91 init();
92 }
93
94 if (SERVICE_PROVIDER_CONFIG == null) {
95 SCIMConf conf = confManager.get();
96
97 SERVICE_PROVIDER_CONFIG = new ServiceProviderConfig(
98 new Meta(
99 Resource.ServiceProviderConfig,
100 conf.getGeneralConf().getCreationDate(),
101 conf.getGeneralConf().getLastChangeDate(),
102 conf.getGeneralConf().getETagValue(),
103 uriBuilder.build().toASCIIString()),
104 new ConfigurationOption(true),
105 new BulkConfigurationOption(
106 false,
107 conf.getGeneralConf().getBulkMaxOperations(),
108 conf.getGeneralConf().getBulkMaxPayloadSize()),
109 new FilterConfigurationOption(true, conf.getGeneralConf().getFilterMaxResults()),
110 new ConfigurationOption(true),
111 new ConfigurationOption(true),
112 new ConfigurationOption(true));
113 SERVICE_PROVIDER_CONFIG.getAuthenticationSchemes().add(new AuthenticationScheme(
114 "JSON Web Token",
115 "Apache Syncope JWT authentication",
116 URI.create("http://www.rfc-editor.org/info/rfc6750"),
117 URI.create("https://syncope.apache.org/docs/"
118 + "reference-guide.html#rest-authentication-and-authorization"),
119 "oauthbearertoken",
120 true));
121 SERVICE_PROVIDER_CONFIG.getAuthenticationSchemes().add(new AuthenticationScheme(
122 "HTTP Basic",
123 "Apache Syncope HTTP Basic authentication",
124 URI.create("http://www.rfc-editor.org/info/rfc2617"),
125 URI.create("https://syncope.apache.org/docs/"
126 + "reference-guide.html#rest-authentication-and-authorization"),
127 "httpbasic",
128 false));
129 }
130 }
131 return SERVICE_PROVIDER_CONFIG;
132 }
133
134 @PreAuthorize("isAuthenticated()")
135 public List<ResourceType> resourceTypes(final UriBuilder uriBuilder) {
136 synchronized (MONITOR) {
137 String uri = uriBuilder.build().toASCIIString();
138 if (USER == null) {
139 USER = new ResourceType("User", "User", "/Users", "User Account", Resource.User.schema(),
140 new Meta(Resource.ResourceType, null, null, null, uri + "User"));
141 USER.getSchemaExtensions().add(new SchemaExtension(Resource.EnterpriseUser.schema(), true));
142 }
143 if (GROUP == null) {
144 GROUP = new ResourceType("Group", "Group", "/Groups", "Group", Resource.Group.schema(),
145 new Meta(Resource.ResourceType, null, null, null, uri + "Group"));
146 }
147 }
148
149 return List.of(USER, GROUP);
150 }
151
152 @PreAuthorize("isAuthenticated()")
153 public ResourceType resourceType(final UriBuilder uriBuilder, final String type) {
154 if (Resource.User.name().equals(type)) {
155 resourceTypes(uriBuilder);
156 return USER;
157 } else if (Resource.Group.name().equals(type)) {
158 resourceTypes(uriBuilder);
159 return GROUP;
160 } else {
161 throw new IllegalArgumentException("Unsupported resource type: " + type);
162 }
163 }
164
165 @PreAuthorize("isAuthenticated()")
166 public String schemas() {
167 synchronized (MONITOR) {
168 if (SCHEMAS == null) {
169 init();
170 }
171 }
172
173 return SCHEMAS;
174 }
175
176 @PreAuthorize("isAuthenticated()")
177 public String schema(final String schema) {
178 synchronized (MONITOR) {
179 if (SCHEMAS == null) {
180 init();
181 }
182 }
183
184 String found = SCHEMA_MAP.get(schema);
185 if (found == null) {
186 throw new NotFoundException("Schema " + schema + " not found");
187 }
188
189 return found;
190 }
191
192 @Override
193 protected EntityTO resolveReference(final Method method, final Object... args)
194 throws UnresolvedReferenceException {
195
196 throw new UnresolvedReferenceException();
197 }
198 }