View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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 }