io.continual.iam.impl.auth0.Auth0IamDb Maven / Gradle / Ivy
The newest version!
package io.continual.iam.impl.auth0;
/*
* Copyright 2019, Continual.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.auth0.client.auth.AuthAPI;
import com.auth0.client.mgmt.ManagementAPI;
import com.auth0.exception.APIException;
import com.auth0.exception.Auth0Exception;
import com.auth0.json.auth.TokenHolder;
import com.auth0.json.mgmt.Role;
import com.auth0.json.mgmt.RolesPage;
import com.auth0.json.mgmt.users.User;
import com.auth0.json.mgmt.users.UsersPage;
import com.auth0.net.AuthRequest;
import io.continual.builder.Builder.BuildFailure;
import io.continual.iam.IamDb;
import io.continual.iam.access.AccessControlList;
import io.continual.iam.access.ProtectedResource;
import io.continual.iam.access.Resource;
import io.continual.iam.credentials.ApiKeyCredential;
import io.continual.iam.credentials.JwtCredential;
import io.continual.iam.credentials.UsernamePasswordCredential;
import io.continual.iam.exceptions.IamBadRequestException;
import io.continual.iam.exceptions.IamGroupDoesNotExist;
import io.continual.iam.exceptions.IamGroupExists;
import io.continual.iam.exceptions.IamIdentityDoesNotExist;
import io.continual.iam.exceptions.IamIdentityExists;
import io.continual.iam.exceptions.IamSvcException;
import io.continual.iam.identity.ApiKey;
import io.continual.iam.identity.Identity;
import io.continual.iam.identity.JwtValidator;
import io.continual.metrics.MetricsCatalog;
import io.continual.util.collections.ShardedExpiringCache;
import io.continual.util.collections.ShardedExpiringCache.Fetcher;
import io.continual.util.collections.ShardedExpiringCache.Fetcher.FetchException;
public class Auth0IamDb implements IamDb
{
public static Auth0IamDb fromJson ( JSONObject config ) throws IamSvcException, BuildFailure
{
return new Auth0IamDb ( config );
}
private Auth0IamDb ( JSONObject config ) throws BuildFailure
{
try
{
fDomain = config.getString ( "domain" );
fClientId = config.getString ( "clientId" );
//fEmailClaim = config.getString ( "emailClaim" );
fAuthApi = new AuthAPI (
fDomain,
fClientId,
config.getString ( "clientSecret" )
);
}
catch ( JSONException x )
{
throw new BuildFailure ( x );
}
fMgmntApi = null;
fMgmtApiToken = null;
fUserCache = new ShardedExpiringCache.Builder ()
.named ( "group cache" )
.cachingFor ( 5, TimeUnit.MINUTES )
.withShardCount ( 32 )
.build ()
;
fGroupCache = new ShardedExpiringCache.Builder ()
.named ( "group cache" )
.cachingFor ( 5, TimeUnit.MINUTES )
.withShardCount ( 32 )
.build ()
;
}
@Override
public boolean userExists ( String userId )throws IamSvcException
{
return loadUser ( userId ) != null;
}
@Override
public boolean userOrAliasExists ( String userIdOrAlias ) throws IamSvcException
{
return userExists ( userIdOrAlias );
}
@Override
public Auth0Identity loadUserOrAlias ( String userIdOrAlias ) throws IamSvcException
{
return loadUser ( userIdOrAlias );
}
@Override
public Collection getAllUsers () throws IamSvcException
{
try
{
final TreeSet result = new TreeSet ();
final UsersPage up = getMgmntApi ().users ().list ( null ).execute ();
for ( User u : up.getItems () )
{
result.add ( u.getEmail () );
}
return result;
}
catch ( Auth0Exception e )
{
throw new IamSvcException ( e );
}
}
@Override
public Map loadAllUsers () throws IamSvcException
{
final HashMap result = new HashMap<> ();
for ( String email : getAllUsers () )
{
result.put ( email, loadUser ( email ) );
}
return result;
}
@Override
public List findUsers ( String startingWith ) throws IamSvcException
{
final LinkedList result = new LinkedList<> ();
for ( String userId : getAllUsers () )
{
if ( userId.startsWith ( startingWith ) )
{
result.add ( userId );
}
}
return result;
}
@Override
public Set getUsersGroups ( String userId ) throws IamSvcException, IamIdentityDoesNotExist
{
final Auth0Identity user = loadUser ( userId );
if ( user != null )
{
return user.getGroupIds ();
}
throw new IamIdentityDoesNotExist ( userId );
}
@Override
public Set getUsersInGroup ( String groupId ) throws IamSvcException, IamGroupDoesNotExist
{
final TreeSet result = new TreeSet<> ();
try
{
final UsersPage up = getMgmntApi().roles ().listUsers ( groupId, null ).execute ();
if ( up == null ) throw new IamGroupDoesNotExist ( groupId ); // not sure this happens
for ( User u : up.getItems () )
{
result.add ( u.getEmail () );
}
}
catch ( APIException e )
{
throw new IamGroupDoesNotExist ( groupId );
}
catch ( Auth0Exception e )
{
throw new IamSvcException ( e );
}
return result;
}
@Override
public Collection getAllGroups () throws IamSvcException
{
try
{
final TreeSet result = new TreeSet ();
final RolesPage up = getMgmntApi ().roles ().list ( null ).execute ();
for ( Auth0Group g : groupsFromRoles ( up ) )
{
result.add ( g.getId () );
}
return result;
}
catch ( Auth0Exception e )
{
throw new IamSvcException ( e );
}
}
@Override
public Auth0Identity loadUser ( String userId ) throws IamSvcException
{
try
{
return fUserCache.read ( userId, null, new Fetcher ()
{
@Override
public Auth0Identity fetch ( String key ) throws FetchException
{
try
{
final List users = getMgmntApi ().users ().listByEmail ( userId, null ).execute ();
if ( users.size () > 0 )
{
final User first = users.get ( 0 );
if ( users.size () > 1 )
{
log.warn ( "Ignoring additional records for {}", first.getEmail () );
}
final RolesPage roles = getMgmntApi().users ().listRoles ( first.getId (), null ).execute ();
final Set groups = groupsFromRoles ( roles );
return new Auth0Identity ( first, groups );
}
return null;
}
catch ( Auth0Exception | IamSvcException e )
{
throw new FetchException ( e );
}
}
} );
}
catch ( FetchException x )
{
throw new IamSvcException ( x );
}
}
@Override
public Auth0Group loadGroup ( String id ) throws IamSvcException
{
try
{
return fGroupCache.read ( id, null, new Fetcher ()
{
@Override
public Auth0Group fetch ( String key ) throws FetchException
{
try
{
final Role role = getMgmntApi().roles ().get ( key ).execute ();
return new Auth0Group ( Auth0IamDb.this, role );
}
catch ( Auth0Exception | IamSvcException e )
{
throw new FetchException ( e );
}
}
} );
}
catch ( FetchException e )
{
throw new IamSvcException ( e );
}
}
@Override
public AccessControlList getAclFor ( Resource resource )
{
if ( resource instanceof ProtectedResource )
{
return ((ProtectedResource) resource ).getAccessControlList ();
}
return null;
}
@Override
public boolean canUser ( String id, Resource resource, String operation ) throws IamSvcException
{
final AccessControlList acl = getAclFor ( resource );
return acl == null ? false : acl.canUser ( loadUser ( id ), operation );
}
@Override
public Auth0Identity createUser ( String userId ) throws IamIdentityExists, IamSvcException
{
readOnlyDbException ();
return null;
}
@Override
public Auth0Identity createAnonymousUser () throws IamSvcException
{
readOnlyDbException ();
return null;
}
@Override
public void deleteUser ( String userId ) throws IamSvcException
{
readOnlyDbException ();
}
@Override
public void addAlias ( String userId, String alias ) throws IamSvcException, IamBadRequestException
{
readOnlyDbException ();
}
@Override
public void removeAlias ( String alias ) throws IamBadRequestException, IamSvcException
{
readOnlyDbException ();
}
@Override
public Collection getAliasesFor ( String userId ) throws IamSvcException, IamIdentityDoesNotExist
{
// no aliases here
return new LinkedList<> ();
}
@Override
public boolean completePasswordReset ( String tag, String newPassword ) throws IamSvcException
{
return false;
}
@Override
public ApiKey loadApiKeyRecord ( String apiKey ) throws IamSvcException
{
return null;
}
@Override
public void restoreApiKey ( ApiKey key ) throws IamIdentityDoesNotExist, IamBadRequestException, IamSvcException
{
readOnlyDbException ();
}
@Override
public void addJwtValidator ( JwtValidator v )
{
log.warn ( "Ignoring added JWT validator in Auth0Db" );
}
@Override
public Auth0Identity authenticate ( UsernamePasswordCredential upc )
{
return null;
}
@Override
public Auth0Identity authenticate ( ApiKeyCredential akc )
{
return null;
}
@Override
public Auth0Identity authenticate ( JwtCredential jwt ) throws IamSvcException
{
// FIXME: wire in auth0 jwt validator
return null;
}
@Override
public String createJwtToken ( Identity ii, long duration, TimeUnit tu ) throws IamSvcException
{
readOnlyDbException ();
return null;
}
@Override
public void invalidateJwtToken ( String jwtToken )
{
}
@Override
public Auth0Group createGroup ( String groupDesc ) throws IamGroupExists, IamSvcException
{
readOnlyDbException ();
return null;
}
@Override
public Auth0Group createGroup ( String groupId, String groupDesc ) throws IamGroupExists, IamSvcException
{
readOnlyDbException ();
return null;
}
@Override
public void addUserToGroup ( String groupId, String userId ) throws IamSvcException, IamIdentityDoesNotExist, IamGroupDoesNotExist
{
readOnlyDbException ();
}
@Override
public void removeUserFromGroup ( String groupId, String userId ) throws IamSvcException, IamIdentityDoesNotExist, IamGroupDoesNotExist
{
readOnlyDbException ();
}
@Override
public String createTag ( String userId, String appTagType, long duration, TimeUnit durationTimeUnit, String nonce ) throws IamSvcException
{
readOnlyDbException ();
return null;
}
@Override
public String getUserIdForTag ( String tag )
{
return null;
}
@Override
public void removeMatchingTag ( String userId, String appTagType )
{
}
@Override
public void sweepExpiredTags ()
{
}
@Override
public void onAclUpdate ( AccessControlList accessControlList )
{
// ignore
}
@Override
public void populateMetrics ( MetricsCatalog metrics )
{
}
private final String fDomain;
private final String fClientId;
private final AuthAPI fAuthApi;
private ManagementAPI fMgmntApi;
private JwtCredential fMgmtApiToken;
private final ShardedExpiringCache fUserCache;
private final ShardedExpiringCache fGroupCache;
private static final Logger log = LoggerFactory.getLogger ( Auth0IamDb.class );
private JSONObject readOnlyDbException () throws IamSvcException
{
throw new IamSvcException ( "Auth0 db is read-only" );
}
private ManagementAPI getMgmntApi () throws IamSvcException
{
if ( fMgmntApi != null && !fMgmtApiToken.isExpired () )
{
return fMgmntApi;
}
try
{
final AuthRequest authRequest = fAuthApi.requestToken ( "https://" + fDomain + "/api/v2/" );
final TokenHolder holder = authRequest.execute ();
final String accessToken = holder.getAccessToken ();
fMgmtApiToken = new JwtCredential ( accessToken );
fMgmntApi = new ManagementAPI ( fDomain, accessToken );
return fMgmntApi;
}
catch ( Auth0Exception | JwtCredential.InvalidJwtToken x )
{
throw new IamSvcException ( x );
}
}
private Set groupsFromRoles ( RolesPage roles )
{
final TreeSet result = new TreeSet<> ();
for ( Role r : roles.getItems () )
{
Auth0Group group = fGroupCache.read ( r.getId () );
if ( group == null )
{
group = new Auth0Group ( this, r );
fGroupCache.write ( r.getId (), group );
}
result.add ( group );
}
return result;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy