You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ka...@apache.org on 2015/01/01 14:31:23 UTC

svn commit: r1648842 - in /directory/escimo/trunk: ldap/src/main/java/org/apache/directory/scim/ldap/ ldap/src/main/resources/ schema/src/main/java/org/apache/directory/scim/schema/ server/src/main/java/org/apache/directory/scim/rest/

Author: kayyagari
Date: Thu Jan  1 13:31:23 2015
New Revision: 1648842

URL: http://svn.apache.org/r1648842
Log:
o updated interface to throw/handle EscimoException
o added more error codes based on the latest 2.0 draft spec
o added a proprty to set DN of user base 

Modified:
    directory/escimo/trunk/ldap/src/main/java/org/apache/directory/scim/ldap/LdapResourceProvider.java
    directory/escimo/trunk/ldap/src/main/resources/ldap-server.properties
    directory/escimo/trunk/schema/src/main/java/org/apache/directory/scim/schema/ErrorCode.java
    directory/escimo/trunk/server/src/main/java/org/apache/directory/scim/rest/ResourceService.java

Modified: directory/escimo/trunk/ldap/src/main/java/org/apache/directory/scim/ldap/LdapResourceProvider.java
URL: http://svn.apache.org/viewvc/directory/escimo/trunk/ldap/src/main/java/org/apache/directory/scim/ldap/LdapResourceProvider.java?rev=1648842&r1=1648841&r2=1648842&view=diff
==============================================================================
--- directory/escimo/trunk/ldap/src/main/java/org/apache/directory/scim/ldap/LdapResourceProvider.java (original)
+++ directory/escimo/trunk/ldap/src/main/java/org/apache/directory/scim/ldap/LdapResourceProvider.java Thu Jan  1 13:31:23 2015
@@ -53,9 +53,9 @@ import org.apache.directory.api.ldap.mod
 import org.apache.directory.api.ldap.model.entry.DefaultEntry;
 import org.apache.directory.api.ldap.model.entry.Entry;
 import org.apache.directory.api.ldap.model.entry.Value;
-import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
 import org.apache.directory.api.ldap.model.exception.LdapEntryAlreadyExistsException;
 import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
 import org.apache.directory.api.ldap.model.filter.ExprNode;
 import org.apache.directory.api.ldap.model.message.LdapResult;
 import org.apache.directory.api.ldap.model.message.ModifyRequest;
@@ -82,19 +82,22 @@ import org.apache.directory.ldap.client.
 import org.apache.directory.ldap.client.api.LdapConnectionConfig;
 import org.apache.directory.ldap.client.api.LdapNetworkConnection;
 import org.apache.directory.scim.AttributeHandler;
-import org.apache.directory.scim.AttributeNotFoundException;
 import org.apache.directory.scim.ComplexAttribute;
 import org.apache.directory.scim.ListResponse;
-import org.apache.directory.scim.MissingParameterException;
 import org.apache.directory.scim.MultiValAttribute;
-import org.apache.directory.scim.ResourceProvider;
 import org.apache.directory.scim.RequestContext;
-import org.apache.directory.scim.ResourceConflictException;
-import org.apache.directory.scim.ResourceNotFoundException;
+import org.apache.directory.scim.ResourceProvider;
 import org.apache.directory.scim.ServerResource;
 import org.apache.directory.scim.SimpleAttribute;
 import org.apache.directory.scim.SimpleAttributeGroup;
-import org.apache.directory.scim.UnauthorizedException;
+import org.apache.directory.scim.exception.AttributeNotFoundException;
+import org.apache.directory.scim.exception.EscimoException;
+import org.apache.directory.scim.exception.InternalException;
+import org.apache.directory.scim.exception.MissingParameterException;
+import org.apache.directory.scim.exception.ResourceConflictException;
+import org.apache.directory.scim.exception.ResourceNotFoundException;
+import org.apache.directory.scim.exception.ResourceUpdateException;
+import org.apache.directory.scim.exception.UnauthorizedException;
 import org.apache.directory.scim.ldap.handlers.LdapAttributeHandler;
 import org.apache.directory.scim.ldap.schema.ComplexType;
 import org.apache.directory.scim.ldap.schema.MultiValType;
@@ -150,7 +153,8 @@ public class LdapResourceProvider implem
 
     private long sessionTimeout = 2 * 60 * 1000;
 
-
+    private String baseDn;
+    
     public LdapResourceProvider()
     {
     }
@@ -162,8 +166,10 @@ public class LdapResourceProvider implem
     }
 
 
-    public void init() throws Exception
+    public void init()
     {
+        //TODO report the initialization error in a better way so that 
+        // the app is not undeployed
         LOG.info( "Initializing LDAP resource provider" );
 
         try
@@ -260,7 +266,7 @@ public class LdapResourceProvider implem
     }
 
 
-    public RequestContext createCtx( UriInfo uriInfo, HttpServletRequest httpReq ) throws Exception
+    public RequestContext createCtx( UriInfo uriInfo, HttpServletRequest httpReq ) throws EscimoException
     {
         LdapConnection connection = getConnection( httpReq );
         LdapRequestContext ctx = new LdapRequestContext( this, connection, uriInfo, httpReq );
@@ -268,7 +274,7 @@ public class LdapResourceProvider implem
     }
 
 
-    private void _initInternal() throws Exception
+    private void _initInternal() throws EscimoException
     {
         if ( initialized )
         {
@@ -283,7 +289,14 @@ public class LdapResourceProvider implem
 
         if ( adminConnection instanceof LdapNetworkConnection )
         {
-            ( ( LdapNetworkConnection ) adminConnection ).loadSchema();// new JarLdifSchemaLoader() );
+            try
+            {
+                ( ( LdapNetworkConnection ) adminConnection ).loadSchema();// new JarLdifSchemaLoader() );
+            }
+            catch( LdapException e )
+            {
+                throw new InternalException( e );
+            }
         }
 
         ldapSchema = adminConnection.getSchemaManager();
@@ -316,7 +329,7 @@ public class LdapResourceProvider implem
     }
 
 
-    private void createConnection() throws IOException, LdapException
+    private void createConnection() throws EscimoException
     {
         LOG.info( "Creating LDAP server connection" );
 
@@ -327,30 +340,38 @@ public class LdapResourceProvider implem
         Properties prop = null;
         InputStream in = null;
 
-        if ( !ldapServerProps.exists() )
+        try
         {
-            in = this.getClass().getClassLoader().getResourceAsStream( ldapServerProps.getName() );
-            FileWriter fw = new FileWriter( ldapServerProps );
-
-            BufferedReader br = new BufferedReader( new InputStreamReader( in ) );
-
-            String s = null;
-
-            while ( ( s = br.readLine() ) != null )
+            if ( !ldapServerProps.exists() )
             {
-                fw.write( s + "\n" );
-            }
-
-            fw.close();
-            br.close();
+                in = this.getClass().getClassLoader().getResourceAsStream( ldapServerProps.getName() );
+                FileWriter fw = new FileWriter( ldapServerProps );
+                
+                BufferedReader br = new BufferedReader( new InputStreamReader( in ) );
+                
+                String s = null;
+                
+                while ( ( s = br.readLine() ) != null )
+                {
+                    fw.write( s + "\n" );
+                }
+                
+                fw.close();
+                br.close();
+            }
+            
+            in = new FileInputStream( ldapServerProps );
+            
+            prop = new Properties();
+            prop.load( in );
+            
+            in.close();
+        }
+        catch( IOException e )
+        {
+            LOG.warn( "Failed to read and store the ldap server properties file {}", ldapServerProps );
+            throw new InternalException( "Failed to read and store the server properties file " + ldapServerProps, e );
         }
-
-        in = new FileInputStream( ldapServerProps );
-
-        prop = new Properties();
-        prop.load( in );
-
-        in.close();
 
         String host = prop.getProperty( "escimo.ldap.server.host" );
         String portVal = prop.getProperty( "escimo.ldap.server.port" );
@@ -359,6 +380,8 @@ public class LdapResourceProvider implem
         String password = prop.getProperty( "escimo.ldap.server.password" );
         String tlsVal = prop.getProperty( "escimo.ldap.server.useTls" );
 
+        baseDn = prop.getProperty( "escimo.ldap.server.users.baseDn", "" );
+        
         config = new LdapConnectionConfig();
         config.setLdapHost( host );
         config.setLdapPort( port );
@@ -367,11 +390,20 @@ public class LdapResourceProvider implem
         config.setCredentials( password );
 
         adminConnection = new LdapNetworkConnection( config );
-        adminConnection.bind();
+        
+        try
+        {
+            adminConnection.bind();
+        }
+        catch( LdapException e )
+        {
+            LOG.warn( "Failed to bind the admin connection", e );
+            throw new InternalException( "Failed to bind the admin connection", e );
+        }
     }
 
 
-    public String authenticate( String userName, String password ) throws Exception
+    public String authenticate( String userName, String password ) throws EscimoException
     {
         _initInternal();
 
@@ -390,13 +422,17 @@ public class LdapResourceProvider implem
 
         try
         {
-            cursor = adminConnection.search( "ou=system", filter, SUBTREE, "1.1" );
+            cursor = adminConnection.search( baseDn, filter, SUBTREE, "1.1" );
 
             if ( cursor.next() )
             {
                 userDn = cursor.get().getDn().getName();
             }
         }
+        catch( Exception e )
+        {
+            LOG.warn( "Failed to find the entry of user {}", userName );
+        }
         finally
         {
             if ( cursor != null )
@@ -416,12 +452,19 @@ public class LdapResourceProvider implem
         {
             conn.bind( userDn, password );
         }
-        catch ( LdapAuthenticationException e )
+        catch ( LdapException e )
         {
             UnauthorizedException ue = new UnauthorizedException( "Cannot authenticate user " + userName + " : "
                 + e.getMessage() );
             ue.initCause( e );
-            conn.close();
+            try
+            {
+                conn.close();
+            }
+            catch( Exception ex )
+            {
+                // ignore
+            }
             throw ue;
         }
 
@@ -516,7 +559,7 @@ public class LdapResourceProvider implem
     }
 
 
-    public ListResponse search( String scimFilter, String attributes, RequestContext ctx ) throws Exception
+    public ListResponse search( String scimFilter, String attributes, RequestContext ctx ) throws EscimoException
     {
         FilterNode filter = FilterParser.parse( scimFilter );
 
@@ -524,47 +567,54 @@ public class LdapResourceProvider implem
 
         ExprNode ldapFilter = null;
 
-        if ( filter != null )
+        try
         {
-            ldapFilter = LdapUtil._scimToLdapFilter( filter, scimSchema, ldapSchema, this );
+            if ( filter != null )
+            {
+                ldapFilter = LdapUtil._scimToLdapFilter( filter, scimSchema, ldapSchema, this );
+            }
+            else
+            {
+                ldapFilter = org.apache.directory.api.ldap.model.filter.FilterParser.parse( scimSchema.getFilter() );
+            }
+            
+            LOG.debug( "LDAP filter {}", ldapFilter );
+            
+            SearchRequest sr = new SearchRequestImpl();
+            sr.setBase( new Dn( scimSchema.getBaseDn() ) );
+            sr.setFilter( ldapFilter );
+            sr.setScope( SearchScope.SUBTREE );
+            
+            String[] requested = getRequestedAttributes( attributes, scimSchema );
+            sr.addAttributes( requested );
+            
+            LdapConnection conn = ( ( LdapRequestContext ) ctx ).getConnection();
+            
+            SearchCursor cursor = conn.search( sr );
+            
+            ListResponse lr = new ListResponse();
+            
+            while ( cursor.next() )
+            {
+                Entry entry = cursor.getEntry();
+                
+                ServerResource res = new ServerResource();
+                
+                ctx.setCoreResource( res );
+                
+                _loadCoreResource( ctx, entry, scimSchema );
+                
+                lr.addResource( res );
+            }
+            
+            cursor.close();
+            
+            return lr;
         }
-        else
+        catch( Exception e )
         {
-            ldapFilter = org.apache.directory.api.ldap.model.filter.FilterParser.parse( scimSchema.getFilter() );
+            throw new InternalException( e );
         }
-
-        LOG.debug( "LDAP filter {}", ldapFilter );
-
-        SearchRequest sr = new SearchRequestImpl();
-        sr.setBase( new Dn( scimSchema.getBaseDn() ) );
-        sr.setFilter( ldapFilter );
-        sr.setScope( SearchScope.SUBTREE );
-
-        String[] requested = getRequestedAttributes( attributes, scimSchema );
-        sr.addAttributes( requested );
-
-        LdapConnection conn = ( ( LdapRequestContext ) ctx ).getConnection();
-
-        SearchCursor cursor = conn.search( sr );
-
-        ListResponse lr = new ListResponse();
-
-        while ( cursor.next() )
-        {
-            Entry entry = cursor.getEntry();
-
-            ServerResource res = new ServerResource();
-
-            ctx.setCoreResource( res );
-
-            _loadCoreResource( ctx, entry, scimSchema );
-
-            lr.addResource( res );
-        }
-
-        cursor.close();
-
-        return lr;
     }
 
 
@@ -622,191 +672,11 @@ public class LdapResourceProvider implem
     }
 
 
-    public ServerResource putResource( String userId, String jsonData, RequestContext ctx ) throws Exception
-    {
-        ResourceSchema resourceSchema = getResourceSchema( ctx );
-        return replaceResource( userId, jsonData, ctx, resourceSchema );
-    }
-
-
-    public ServerResource patchResource( String userId, String jsonData, RequestContext ctx ) throws Exception
-    {
-        ResourceSchema resourceSchema = getResourceSchema( ctx );
-        return patchResource( userId, jsonData, ctx, resourceSchema );
-    }
-
-
-    public InputStream getUserPhoto( String id, String atName, RequestContext ctx ) throws MissingParameterException
-    {
-        if ( Strings.isEmpty( id ) )
-        {
-            throw new MissingParameterException( "parameter 'id' cannot be null or empty" );
-        }
-
-        if ( Strings.isEmpty( atName ) )
-        {
-            throw new MissingParameterException( "parameter 'atName' cannot be null or empty" );
-        }
-
-        ResourceSchema resourceSchema = getResourceSchema( ctx );
-        Entry entry = fetchEntryById( id, resourceSchema, ctx );
-
-        if ( entry == null )
-        {
-            return null;
-        }
-
-        Attribute phtoAt = entry.get( atName );
-
-        if ( phtoAt == null )
-        {
-            return null;
-        }
-
-        ByteArrayInputStream bin = new ByteArrayInputStream( phtoAt.get().getBytes() );
-
-        return bin;
-    }
-
-
-    private void addAttributes( Entry entry, JsonObject obj, RequestContext ctx, ResourceSchema resourceSchema )
-        throws Exception
-    {
-        //obj.remove( "schemas" );
-
-        for ( java.util.Map.Entry<String, JsonElement> e : obj.entrySet() )
-        {
-            String name = e.getKey();
-
-            if ( name.startsWith( "urn:scim:schemas:" ) )
-            {
-                continue;
-            }
-
-            BaseType bt = resourceSchema.getAttribute( name );
-
-            if ( bt == null )
-            {
-                LOG.debug( "Unknown attribute name "
-                    + name
-                    + " is present in the JSON payload that has no corresponding mapping in the escimo-ldap-mapping.xml file" );
-                continue;
-            }
-
-            if ( bt.isReadOnly() )
-            {
-                continue;
-            }
-
-            AttributeHandler handler = bt.getHandler();
-
-            if ( handler != null )
-            {
-                handler.write( bt, e.getValue(), entry, ctx );
-            }
-            else
-            {
-                LdapUtil.scimToLdapAttribute( bt, e.getValue(), entry, ctx );
-            }
-        }
-    }
-
-
-    public ServerResource addResource( String json, RequestContext ctx ) throws Exception
-    {
-        String userName = null;
-
-        try
-        {
-            JsonParser parser = new JsonParser();
-            JsonObject obj = ( JsonObject ) parser.parse( json );
-
-            Entry entry = new DefaultEntry( ldapSchema );
-
-            ResourceSchema resourceSchema = getResourceSchema( ctx );
-            SimpleType st = resourceSchema.getRdnType();
-            String userIdName = st.getMappedTo();
-
-            String dn = ctx.getReqHeaderValue( ENTRYDN_HEADER );
-
-            if ( Strings.isEmpty( dn ) )
-            {
-                dn = null;
-            }
-
-            if ( dn == null )
-            {
-                userName = obj.get( st.getName() ).getAsString();
-
-                dn = userIdName + "=" + userName + "," + resourceSchema.getBaseDn();
-            }
-
-            _resourceToEntry( entry, obj, ctx, resourceSchema );
-
-            entry.setDn( dn );
-
-            LdapConnection conn = ( ( LdapRequestContext ) ctx ).getConnection();
-
-            conn.add( entry );
-
-            entry = conn.lookup( entry.getDn(), SchemaConstants.ALL_ATTRIBUTES_ARRAY );
-
-            ServerResource addedUser = new ServerResource();
-
-            ctx.setCoreResource( addedUser );
-
-            _loadCoreResource( ctx, entry, resourceSchema );
-
-            return addedUser;
-
-        }
-        catch ( LdapEntryAlreadyExistsException e )
-        {
-            String message = "Resource already exists, conflicting attribute userName : " + userName;
-            throw new ResourceConflictException( message );
-        }
-        catch ( Exception e )
-        {
-            LOG.warn( "Failed to create User resource", e );
-            throw e;
-        }
-    }
-
-
-    private void _resourceToEntry( Entry entry, JsonObject obj, RequestContext ctx, ResourceSchema resourceSchema )
-        throws Exception
-    {
-
-        // add the objectClasses first so a handler will get a chance to
-        // inspect what attributes can the entry hold
-        // e.x it is useful for handling Groups, where the handler can
-        // find if the attribute name is 'member' or 'uniqueMember'
-        for ( String oc : resourceSchema.getObjectClasses() )
-        {
-            entry.add( SchemaConstants.OBJECT_CLASS, oc );
-        }
-
-        // process the core attributes first
-        addAttributes( entry, obj, ctx, resourceSchema );
-
-        List<String> uris = resourceSchema.getSchemaIds();
-
-        for ( String u : uris )
-        {
-            JsonObject userAtObj = ( JsonObject ) obj.get( u );
-            if ( userAtObj != null )
-            {
-                addAttributes( entry, userAtObj, ctx, resourceSchema );
-            }
-        }
-
-    }
-
-
     // TODO can userName be changed for a user?? likewise displayName for a Group
-    public ServerResource replaceResource( String resourceId, String jsonData, RequestContext ctx,
-        ResourceSchema resourceSchema ) throws Exception
+    public ServerResource putResource( String resourceId, String jsonData, RequestContext ctx ) throws EscimoException
     {
+        ResourceSchema resourceSchema = getResourceSchema( ctx );
+        
         JsonParser parser = new JsonParser();
         JsonObject obj = ( JsonObject ) parser.parse( jsonData );
 
@@ -820,36 +690,44 @@ public class LdapResourceProvider implem
         Attribute existingPwdAt = existingEntry.get( SchemaConstants.USER_PASSWORD_AT );
         Attribute newPwdAt = entry.get( SchemaConstants.USER_PASSWORD_AT );
 
-        if ( existingPwdAt != null )
-        {
-            existingEntry.remove( existingPwdAt );
-        }
-
-        if ( newPwdAt != null )
-        {
-            entry.remove( newPwdAt );
-        }
-
-        Attribute existingUserNameAt = null;
         Attribute newUserNameAt = null;
-        SimpleType st = resourceSchema.getRdnType();
+        Attribute existingUserNameAt = null;
 
-        if ( st != null )
+        try
         {
-            existingUserNameAt = existingEntry.get( st.getMappedTo() );
-
-            if ( existingUserNameAt != null )
+            if ( existingPwdAt != null )
             {
-                existingEntry.remove( existingUserNameAt );
+                existingEntry.remove( existingPwdAt );
             }
-
-            newUserNameAt = entry.get( st.getMappedTo() );
-
-            if ( newUserNameAt != null )
+            
+            if ( newPwdAt != null )
+            {
+                entry.remove( newPwdAt );
+            }
+            
+            SimpleType st = resourceSchema.getRdnType();
+            
+            if ( st != null )
             {
-                entry.remove( newUserNameAt );
+                existingUserNameAt = existingEntry.get( st.getMappedTo() );
+                
+                if ( existingUserNameAt != null )
+                {
+                    existingEntry.remove( existingUserNameAt );
+                }
+                
+                newUserNameAt = entry.get( st.getMappedTo() );
+                
+                if ( newUserNameAt != null )
+                {
+                    entry.remove( newUserNameAt );
+                }
             }
         }
+        catch( LdapException e )
+        {
+            throw new InternalException( e );
+        }
 
         ModifyRequest modReq = new ModifyRequestImpl();
         modReq.setName( existingEntry.getDn() );
@@ -899,23 +777,38 @@ public class LdapResourceProvider implem
 
         LdapConnection conn = ( ( LdapRequestContext ) ctx ).getConnection();
 
-        ModifyResponse modResp = conn.modify( modReq );
-
-        if ( modResp.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS )
+        try
         {
-            throw new Exception( "Failed to replace the resource " + modResp.getLdapResult().getDiagnosticMessage() );
+            ModifyResponse modResp = conn.modify( modReq );
+            
+            if ( modResp.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS )
+            {
+                throw new ResourceUpdateException( "Failed to replace the resource " + modResp.getLdapResult().getDiagnosticMessage() );
+            }
+        }
+        catch( LdapException e )
+        {
+            throw new InternalException( e );
         }
 
-        if ( newUserNameAt != null )
+        try
         {
-            if ( !existingUserNameAt.contains( newUserNameAt.getString() ) )
+            if ( newUserNameAt != null )
             {
-                // a modDN needs to be performed
-                conn.rename( existingEntry.getDn().getName(),
-                    newUserNameAt.getUpId() + "=" + newUserNameAt.getString(), true );
+                if ( !existingUserNameAt.contains( newUserNameAt.getString() ) )
+                {
+                    // a modDN needs to be performed
+                    conn.rename( existingEntry.getDn().getName(),
+                        newUserNameAt.getUpId() + "=" + newUserNameAt.getString(), true );
+                }
             }
         }
-
+        catch( LdapException e )
+        {
+            LOG.warn( "Failed to rename the resource {}", resourceId, e );
+            throw new ResourceUpdateException( "Failed to rename the resource " + resourceId, e );
+        }
+        
         entry = fetchEntryById( resourceId, resourceSchema, ctx );
 
         ServerResource resource = new ServerResource();
@@ -928,9 +821,9 @@ public class LdapResourceProvider implem
     }
 
 
-    public ServerResource patchResource( String resourceId, String jsonData, RequestContext ctx,
-        ResourceSchema resourceSchema ) throws Exception
+    public ServerResource patchResource( String resourceId, String jsonData, RequestContext ctx ) throws EscimoException
     {
+        ResourceSchema resourceSchema = getResourceSchema( ctx );
         JsonParser parser = new JsonParser();
         JsonObject obj = ( JsonObject ) parser.parse( jsonData );
 
@@ -960,10 +853,22 @@ public class LdapResourceProvider implem
                         throw new AttributeNotFoundException( "No definition found for the attribute " + name );
                     }
 
+                    if ( bt.isReadOnly() )
+                    {
+                        continue;
+                    }
+                    
                     AttributeHandler handler = bt.getHandler();
                     if ( handler != null )
                     {
-                        handler.deleteAttribute( bt, existingEntry, ctx, modReq );
+                        try
+                        {
+                            handler.deleteAttribute( bt, existingEntry, ctx, modReq );
+                        }
+                        catch( Exception ex )
+                        {
+                            throw new InternalException( ex );
+                        }
                         continue;
                     }
 
@@ -983,7 +888,8 @@ public class LdapResourceProvider implem
             LdapResult result = modResp.getLdapResult();
             if ( result.getResultCode() != ResultCodeEnum.SUCCESS )
             {
-                throw new Exception( result.getDiagnosticMessage() );
+                LOG.debug( "Failed to patch the resource with ID {}, LDAP error result {}", resourceId, result );
+                throw new ResourceUpdateException( "Failed to patch the resource with ID " + resourceId );
             }
 
             // send attributes if requested
@@ -1005,39 +911,241 @@ public class LdapResourceProvider implem
         catch ( Exception e )
         {
             LOG.warn( "Failed to patch the resource with ID {}", resourceId, e );
-            throw e;
+            throw new ResourceUpdateException( "Failed to patch the resource with ID " + resourceId, e );
         }
     }
 
 
-    public void deleteResource( String id, RequestContext ctx ) throws Exception
+    public InputStream getUserPhoto( String id, String atName, RequestContext ctx ) throws MissingParameterException
     {
+        if ( Strings.isEmpty( id ) )
+        {
+            throw new MissingParameterException( "parameter 'id' cannot be null or empty" );
+        }
+
+        if ( Strings.isEmpty( atName ) )
+        {
+            throw new MissingParameterException( "parameter 'atName' cannot be null or empty" );
+        }
+
         ResourceSchema resourceSchema = getResourceSchema( ctx );
-        deleteResource( id, resourceSchema, ctx );
+        Entry entry = fetchEntryById( id, resourceSchema, ctx );
+
+        if ( entry == null )
+        {
+            return null;
+        }
+
+        Attribute phtoAt = entry.get( atName );
+
+        if ( phtoAt == null )
+        {
+            return null;
+        }
+
+        ByteArrayInputStream bin = new ByteArrayInputStream( phtoAt.get().getBytes() );
+
+        return bin;
     }
 
 
-    private void deleteResource( String id, ResourceSchema schema, RequestContext ctx ) throws LdapException
+    private void addAttributes( Entry entry, JsonObject obj, RequestContext ctx, ResourceSchema resourceSchema )
+        throws EscimoException
     {
-        Entry entry = fetchEntryById( id, schema, ctx );
-        LdapConnection conn = ( ( LdapRequestContext ) ctx ).getConnection();
-        conn.delete( entry.getDn() );
+        try
+        {
+            for ( java.util.Map.Entry<String, JsonElement> e : obj.entrySet() )
+            {
+                String name = e.getKey();
+                
+                if ( name.startsWith( "urn:scim:schemas:" ) )
+                {
+                    continue;
+                }
+                
+                BaseType bt = resourceSchema.getAttribute( name );
+                
+                if ( bt == null )
+                {
+                    LOG.debug( "Unknown attribute name {} is present in the JSON payload that has no corresponding mapping in the escimo-ldap-mapping.xml file", name );
+                    continue;
+                }
+                
+                if ( bt.isReadOnly() )
+                {
+                    continue;
+                }
+                
+                AttributeHandler handler = bt.getHandler();
+                
+                if ( handler != null )
+                {
+                    handler.write( bt, e.getValue(), entry, ctx );
+                }
+                else
+                {
+                    LdapUtil.scimToLdapAttribute( bt, e.getValue(), entry, ctx );
+                }
+            }
+        }
+        catch( Exception e )
+        {
+            throw new InternalException( e );
+        }
     }
 
 
-    private void _loadCoreResource( RequestContext ctx, Entry entry, ResourceSchema resourceSchema ) throws Exception
+    public ServerResource addResource( String json, RequestContext ctx ) throws EscimoException
     {
-        ServerResource resource = ctx.getCoreResource();
+        String userName = null;
 
-        // first fill in the id, we need this for deriving location
-        SimpleType idType = ( SimpleType ) resourceSchema.getCoreAttribute( "id" );
-        SimpleAttribute idAttribute = getValueForSimpleType( idType, entry, ctx );
-        resource.addAttribute( idType.getUri(), idAttribute );
+        try
+        {
+            JsonParser parser = new JsonParser();
+            JsonObject obj = ( JsonObject ) parser.parse( json );
 
-        resource.setId( ( String ) idAttribute.getValue() );
+            Entry entry = new DefaultEntry( ldapSchema );
 
-        _loadAttributes( ctx, entry, resourceSchema.getCoreTypes(), idType );
-        _loadAttributes( ctx, entry, resourceSchema.getExtendedTypes(), idType );
+            ResourceSchema resourceSchema = getResourceSchema( ctx );
+            SimpleType st = resourceSchema.getRdnType();
+            String userIdName = st.getMappedTo();
+
+            String dn = ctx.getReqHeaderValue( ENTRYDN_HEADER );
+
+            if ( Strings.isEmpty( dn ) )
+            {
+                dn = null;
+            }
+
+            if ( dn == null )
+            {
+                userName = obj.get( st.getName() ).getAsString();
+
+                dn = userIdName + "=" + userName + "," + resourceSchema.getBaseDn();
+            }
+
+            _resourceToEntry( entry, obj, ctx, resourceSchema );
+
+            entry.setDn( dn );
+
+            LdapConnection conn = ( ( LdapRequestContext ) ctx ).getConnection();
+
+            conn.add( entry );
+
+            entry = conn.lookup( entry.getDn(), SchemaConstants.ALL_ATTRIBUTES_ARRAY );
+
+            ServerResource addedUser = new ServerResource();
+
+            ctx.setCoreResource( addedUser );
+
+            _loadCoreResource( ctx, entry, resourceSchema );
+
+            return addedUser;
+
+        }
+        catch ( LdapEntryAlreadyExistsException e )
+        {
+            String message = "Resource already exists, conflicting attribute userName : " + userName;
+            throw new ResourceConflictException( message );
+        }
+        catch ( Exception e )
+        {
+            LOG.warn( "Failed to create User resource", e );
+            throw new InternalException( e );
+        }
+    }
+
+
+    private void _resourceToEntry( Entry entry, JsonObject obj, RequestContext ctx, ResourceSchema resourceSchema )
+        throws EscimoException
+    {
+
+        // add the objectClasses first so a handler will get a chance to
+        // inspect what attributes can the entry hold
+        // e.x it is useful for handling Groups, where the handler can
+        // find if the attribute name is 'member' or 'uniqueMember'
+        try
+        {
+            for ( String oc : resourceSchema.getObjectClasses() )
+            {
+                entry.add( SchemaConstants.OBJECT_CLASS, oc );
+            }
+        }
+        catch( LdapException e )
+        {
+            throw new InternalException( e );
+        }
+
+        // process the core attributes first
+        addAttributes( entry, obj, ctx, resourceSchema );
+
+        List<String> uris = resourceSchema.getSchemaIds();
+
+        for ( String u : uris )
+        {
+            JsonObject userAtObj = ( JsonObject ) obj.get( u );
+            if ( userAtObj != null )
+            {
+                addAttributes( entry, userAtObj, ctx, resourceSchema );
+            }
+        }
+
+    }
+
+
+    public void deleteResource( String id, RequestContext ctx ) throws EscimoException
+    {
+        ResourceSchema resourceSchema = getResourceSchema( ctx );
+        Entry entry = fetchEntryById( id, resourceSchema, ctx );
+        if( entry == null )
+        {
+            LOG.debug( "No resource found with the id {}", id );
+            throw new ResourceNotFoundException( "Resource with id " + id + " not found" );
+        }
+        
+        LdapConnection conn = ( ( LdapRequestContext ) ctx ).getConnection();
+        try
+        {
+            conn.delete( entry.getDn() );
+        }
+        catch( LdapNoSuchObjectException e )
+        {
+            LOG.debug( "No resource found with the id {}", id );
+            throw new ResourceNotFoundException( "Resource with id " + id + " not found" );
+        }
+        catch( LdapException e )
+        {
+            LOG.warn( "Failed to delete the resource with id {}", id );
+            throw new InternalException( e );
+        }
+    }
+
+
+    private void _loadCoreResource( RequestContext ctx, Entry entry, ResourceSchema resourceSchema ) throws EscimoException
+    {
+        ServerResource resource = ctx.getCoreResource();
+
+        if ( entry == null )
+        {
+            return;
+        }
+        
+        try
+        {
+            // first fill in the id, we need this for deriving location
+            SimpleType idType = ( SimpleType ) resourceSchema.getCoreAttribute( "id" );
+            SimpleAttribute idAttribute = getValueForSimpleType( idType, entry, ctx );
+            resource.addAttribute( idType.getUri(), idAttribute );
+            
+            resource.setId( ( String ) idAttribute.getValue() );
+            
+            _loadAttributes( ctx, entry, resourceSchema.getCoreTypes(), idType );
+            _loadAttributes( ctx, entry, resourceSchema.getExtendedTypes(), idType );
+        }
+        catch( Exception e )
+        {
+            throw new InternalException( e );
+        }
     }
 
 
@@ -1342,7 +1450,7 @@ public class LdapResourceProvider implem
     }
 
 
-    public LdapConnection getConnection( HttpServletRequest httpReq ) throws Exception
+    public LdapConnection getConnection( HttpServletRequest httpReq ) throws EscimoException
     {
 
         if ( allowAuthorizedUsers )

Modified: directory/escimo/trunk/ldap/src/main/resources/ldap-server.properties
URL: http://svn.apache.org/viewvc/directory/escimo/trunk/ldap/src/main/resources/ldap-server.properties?rev=1648842&r1=1648841&r2=1648842&view=diff
==============================================================================
--- directory/escimo/trunk/ldap/src/main/resources/ldap-server.properties (original)
+++ directory/escimo/trunk/ldap/src/main/resources/ldap-server.properties Thu Jan  1 13:31:23 2015
@@ -20,4 +20,5 @@ escimo.ldap.server.port = 10389
 escimo.ldap.server.user = uid=admin,ou=system
 escimo.ldap.server.password = secret
 escimo.ldap.server.useTls = false
+escimo.ldap.server.users.baseDn = ou=system
 escimo.resource.provider = org.apache.directory.scim.ldap.LdapResourceProvider

Modified: directory/escimo/trunk/schema/src/main/java/org/apache/directory/scim/schema/ErrorCode.java
URL: http://svn.apache.org/viewvc/directory/escimo/trunk/schema/src/main/java/org/apache/directory/scim/schema/ErrorCode.java?rev=1648842&r1=1648841&r2=1648842&view=diff
==============================================================================
--- directory/escimo/trunk/schema/src/main/java/org/apache/directory/scim/schema/ErrorCode.java (original)
+++ directory/escimo/trunk/schema/src/main/java/org/apache/directory/scim/schema/ErrorCode.java Thu Jan  1 13:31:23 2015
@@ -27,31 +27,66 @@ package org.apache.directory.scim.schema
 public enum ErrorCode
 {
 
-    BAD_REQUEST(400, "Request is unparseable, syntactically incorrect, or violates schema"),
+    TEMPORARY_REDIRECT(307, null, " The client is directed to repeat the same HTTP request at the location identified."
+                            + " The client  SHOULD NOT use the location provided in the response as a permanent reference to the"
+                            + " resource and SHOULD continue to use the original request URI"),
+                            
+    PERMANENT_REDIRECT(308, null, "The client is directed to repeat the same HTTP request at the location identified. The client"
+                            + " SHOULD use the location provided in the response as the permanent reference to the resource"),
+                            
+    BAD_REQUEST(400, null, "Request is unparseable, syntactically incorrect, or violates schema"),
     
-    UNAUTHORIZED(401, "Authorization failure"),
+    UNAUTHORIZED(401, null, "Authorization failure"),
     
-    FORBIDDEN(403, "Server does not support requested operation"),
+    FORBIDDEN(403, null, "Server does not support requested operation"),
     
-    NOT_FOUND(404, "Specified resource does not exist"),
+    NOT_FOUND(404, null, "Specified resource does not exist"),
     
-    CONFLICT(409, "The specified version number does not match the resource's latest version number or a Service Provider refused to create a new, duplicate resource"),
+    CONFLICT(409, null, "The specified version number does not match the resource's latest version number or a Service Provider refused"
+                        + " to create a new, duplicate resource"),
     
-    PRECONDITION_FAILED(412, "Failed to update as Resource changed on the server since last retrieved"),
+    PRECONDITION_FAILED(412, null, "Failed to update as Resource changed on the server since last retrieved"),
     
-    REQUEST_ENTITY_TOO_LARGE(413, "Requested entity too large"),
+    REQUEST_ENTITY_TOO_LARGE(413, null, "Requested entity too large"),
     
-    INTERNAL_SERVER_ERROR(500, "Internal server error"),
+    INTERNAL_SERVER_ERROR(500, null, "Internal server error"),
     
-    NOT_IMPLEMENTED(501, "Service Provider does not support the requested operation");
+    NOT_IMPLEMENTED(501, null, "Service Provider does not support the requested operation"),
+    
+    BAD_REQUEST_INVALID_FILTER(400, "invalidFilter", "The specified filter syntax was invalid (does not comply with Figure 1) or the "
+                                                     + "specified attribute and filter comparison combination is not supported."),
+    
+    BAD_REQUEST_TOO_MANY(400, "tooMany", "The specified filter yields many more results than the server is willing calculate or process."
+                                         + " For example, a filter such as \"(userName pr)\" by itself would return all entries with a "
+                                         + "\"userName\" and MAY not be acceptable to the service provider."),
+    
+    BAD_REQUEST_UNIQUENESS(400, "uniqueness", "One or more of attribute values is already in use or is reserved."),
+    
+    BAD_REQUEST_MUTABILITY(400, "mutability", "The attempted modification is not compatible with the target attributes mutability or current"
+                                              + " state (e.g. modification of an immutable attribute with an existing value)."),
+    
+    BAD_REQUEST_INVALID_SYNTAX(400, "invalidSyntax", "The request body message structure was invalid or did not conform to the request schema."),
+
+    BAD_REQUEST_INVALID_PATH(400, "invalidPath", "The path attribute was invalid or malformed (see Figure 7)."),
+    
+    BAD_REQUEST_NOTARGET(400, "noTarget", "The specified \"path\" did not yield an attribute or attribute value that could be operated on. "
+                                          + "This occurs when the specified \"path\" value contains a filter that yields no match."),
+    
+    BAD_REQUEST_INVALID_VALUE(400, "invalidValue", "A required value was missing, or the value specified was not compatible with the operation"
+                                                   + " or attribute type."),
+    
+    BAD_REQUEST_INVALID_VERSION(400, "invalidVers", "The specified SCIM protocol version is not supported");
     
     private int val;
     
     private String desc;
     
-    private ErrorCode( int val, String desc )
+    private String scimType;
+    
+    private ErrorCode( int val, String scimType, String desc )
     {
         this.val = val;
+        this.scimType = scimType;
         this.desc = desc;
     }
 
@@ -63,6 +98,16 @@ public enum ErrorCode
         return val;
     }
 
+    
+    /**
+     * @return the scimType desc
+     */
+    String getScimType()
+    {
+        return scimType;
+    }
+
+    
     /**
      * @return the desc
      */

Modified: directory/escimo/trunk/server/src/main/java/org/apache/directory/scim/rest/ResourceService.java
URL: http://svn.apache.org/viewvc/directory/escimo/trunk/server/src/main/java/org/apache/directory/scim/rest/ResourceService.java?rev=1648842&r1=1648841&r2=1648842&view=diff
==============================================================================
--- directory/escimo/trunk/server/src/main/java/org/apache/directory/scim/rest/ResourceService.java (original)
+++ directory/escimo/trunk/server/src/main/java/org/apache/directory/scim/rest/ResourceService.java Thu Jan  1 13:31:23 2015
@@ -120,7 +120,7 @@ public class ResourceService extends Abs
     
     @POST
     @Produces({MediaType.APPLICATION_JSON})
-    public Response addUser( String jsonData, @Context UriInfo uriInfo )
+    public Response addResource( String jsonData, @Context UriInfo uriInfo )
     {
         ResponseBuilder rb = null;