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 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 }