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 static List<ResourceType> resourceTypes(final UriBuilder uriBuilder) {
136 synchronized (MONITOR) {
137 if (USER == null) {
138 USER = new ResourceType("User", "User", "/Users", "User Account", Resource.User.schema(),
139 new Meta(Resource.ResourceType,
140 null, null, null, uriBuilder.path("User").build().toASCIIString()));
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,
146 null, null, null, uriBuilder.path("Group").build().toASCIIString()));
147 }
148 }
149
150 return List.of(USER, GROUP);
151 }
152
153 @PreAuthorize("isAuthenticated()")
154 public static ResourceType resourceType(final UriBuilder uriBuilder, final String type) {
155 if (Resource.User.name().equals(type)) {
156 resourceTypes(uriBuilder);
157 return USER;
158 } else if (Resource.Group.name().equals(type)) {
159 resourceTypes(uriBuilder);
160 return GROUP;
161 } else {
162 throw new IllegalArgumentException("Unsupported resource type: " + type);
163 }
164 }
165
166 @PreAuthorize("isAuthenticated()")
167 public String schemas() {
168 synchronized (MONITOR) {
169 if (SCHEMAS == null) {
170 init();
171 }
172 }
173
174 return SCHEMAS;
175 }
176
177 @PreAuthorize("isAuthenticated()")
178 public String schema(final String schema) {
179 synchronized (MONITOR) {
180 if (SCHEMAS == null) {
181 init();
182 }
183 }
184
185 String found = SCHEMA_MAP.get(schema);
186 if (found == null) {
187 throw new NotFoundException("Schema " + schema + " not found");
188 }
189
190 return found;
191 }
192
193 @Override
194 protected EntityTO resolveReference(final Method method, final Object... args)
195 throws UnresolvedReferenceException {
196
197 throw new UnresolvedReferenceException();
198 }
199 }