public class AuthDaoImpl extends AbstractJooqDao implements AuthDao { @Inject GenericResourceDao resourceDao; @Inject ObjectManager objectManager; @Inject ObjectProcessManager objectProcessManager; @Inject LockManager lockManager; private DynamicStringListProperty SUPPORTED_TYPES = ArchaiusUtil.getList("account.by.key.credential.types"); @Override public Account getAdminAccount() { return create() .selectFrom(ACCOUNT) .where( ACCOUNT .STATE .eq(CommonStatesConstants.ACTIVE) .and(ACCOUNT.KIND.eq(AccountConstants.ADMIN_KIND))) .orderBy(ACCOUNT.ID.asc()) .limit(1) .fetchOne(); } @Override public List<Account> searchUsers(String username) { return create() .select(ACCOUNT.fields()) .from(ACCOUNT) .join(CREDENTIAL) .on(CREDENTIAL.ACCOUNT_ID.eq(ACCOUNT.ID)) .where( ACCOUNT .STATE .eq(CommonStatesConstants.ACTIVE) .and(CREDENTIAL.STATE.eq(CommonStatesConstants.ACTIVE)) .and(CREDENTIAL.PUBLIC_VALUE.contains(username)) .and(CREDENTIAL.KIND.eq(CredentialConstants.KIND_PASSWORD))) .orderBy(ACCOUNT.ID.asc()) .fetchInto(Account.class); } @Override public Account getByUsername(String username) { try { return create() .select(ACCOUNT.fields()) .from(ACCOUNT) .join(CREDENTIAL) .on(CREDENTIAL.ACCOUNT_ID.eq(ACCOUNT.ID)) .where( ACCOUNT .STATE .eq(CommonStatesConstants.ACTIVE) .and(CREDENTIAL.STATE.eq(CommonStatesConstants.ACTIVE)) .and(CREDENTIAL.PUBLIC_VALUE.eq(username))) .and(CREDENTIAL.KIND.eq(CredentialConstants.KIND_PASSWORD)) .fetchOneInto(AccountRecord.class); } catch (InvalidResultException e) { throw new ClientVisibleException(ResponseCodes.CONFLICT, "MultipleOfUsername"); } } @Override public Account getAccountByLogin(String publicValue, String secretValue) { Credential credential = create() .selectFrom(CREDENTIAL) .where(CREDENTIAL.STATE.eq(CommonStatesConstants.ACTIVE)) .and( CREDENTIAL .PUBLIC_VALUE .eq(publicValue) .and(CREDENTIAL.KIND.equalIgnoreCase(CredentialConstants.KIND_PASSWORD))) .fetchOne(); if (credential == null) { return null; } boolean secretIsCorrect = ApiContext.getContext() .getTransformationService() .compare(secretValue, credential.getSecretValue()); if (secretIsCorrect) { return create() .selectFrom(ACCOUNT) .where( ACCOUNT .ID .eq(credential.getAccountId()) .and(ACCOUNT.STATE.eq(CommonStatesConstants.ACTIVE))) .fetchOneInto(AccountRecord.class); } else { return null; } } @Override public Account getAccountById(Long id) { return create() .selectFrom(ACCOUNT) .where( ACCOUNT .ID .eq(id) .and(ACCOUNT.STATE.ne(CommonStatesConstants.PURGED)) .and(ACCOUNT.REMOVED.isNull())) .fetchOne(); } @Override public Account getAccountByKeys(String access, String secretKey) { try { Credential credential = create() .selectFrom(CREDENTIAL) .where(CREDENTIAL.STATE.eq(CommonStatesConstants.ACTIVE)) .and(CREDENTIAL.PUBLIC_VALUE.eq(access)) .and(CREDENTIAL.KIND.in(SUPPORTED_TYPES.get())) .fetchOne(); if (credential == null) { return null; } boolean secretIsCorrect = ApiContext.getContext() .getTransformationService() .compare(secretKey, credential.getSecretValue()); if (secretIsCorrect) { return create() .selectFrom(ACCOUNT) .where( ACCOUNT .ID .eq(credential.getAccountId()) .and(ACCOUNT.STATE.eq(CommonStatesConstants.ACTIVE))) .fetchOneInto(AccountRecord.class); } else { return null; } } catch (InvalidResultException e) { throw new ClientVisibleException(ResponseCodes.CONFLICT, "MultipleKeys"); } } @Override public Account getAccountByExternalId(String externalId, String externalType) { return create() .selectFrom(ACCOUNT) .where( ACCOUNT .EXTERNAL_ID .eq(externalId) .and(ACCOUNT.EXTERNAL_ID_TYPE.eq(externalType)) .and(ACCOUNT.STATE.ne("purged"))) .orderBy(ACCOUNT.ID.asc()) .fetchOne(); } @Override public Account createAccount(String name, String kind, String externalId, String externalType) { Account account = getAccountByExternalId(externalId, externalType); if (account != null) { throw new ClientVisibleException(ResponseCodes.UNAUTHORIZED); } Map<Object, Object> properties = new HashMap<>(); if (StringUtils.isNotEmpty(name)) { properties.put(ACCOUNT.NAME, name); } if (StringUtils.isNotEmpty(kind)) { properties.put(ACCOUNT.KIND, kind); } if (StringUtils.isNotEmpty(externalId)) { properties.put(ACCOUNT.EXTERNAL_ID, externalId); } if (StringUtils.isNotEmpty(externalType)) { properties.put(ACCOUNT.EXTERNAL_ID_TYPE, externalType); } return resourceDao.createAndSchedule( Account.class, objectManager.convertToPropertiesFor(Account.class, properties)); } @Override public Identity getIdentity(Long id) { Account account = getAccountById(id); if (account == null || account.getKind().equalsIgnoreCase(ProjectConstants.TYPE)) { return null; } Credential credential = create() .selectFrom(CREDENTIAL) .where( CREDENTIAL .KIND .equalIgnoreCase(CredentialConstants.KIND_PASSWORD) .and(CREDENTIAL.ACCOUNT_ID.eq(id)) .and(CREDENTIAL.STATE.equalIgnoreCase(CommonStatesConstants.ACTIVE))) .fetchAny(); String accountId = (String) ApiContext.getContext() .getIdFormatter() .formatId(objectManager.getType(Account.class), account.getId()); return new Identity( ProjectConstants.RANCHER_ID, accountId, account.getName(), null, null, credential == null ? null : credential.getPublicValue()); } @Override public Account createProject(String name, String description) { Map<Object, Object> properties = new HashMap<>(); if (name != null) { properties.put(ACCOUNT.NAME, name); } if (description != null) { properties.put(ACCOUNT.DESCRIPTION, description); } properties.put(ACCOUNT.KIND, ProjectConstants.TYPE); return resourceDao.createAndSchedule( Account.class, objectManager.convertToPropertiesFor(Account.class, properties)); } @Override public Account getAccountByUuid(String uuid) { return create() .selectFrom(ACCOUNT) .where(ACCOUNT.UUID.eq(uuid).and(ACCOUNT.STATE.eq(CommonStatesConstants.ACTIVE))) .orderBy(ACCOUNT.ID.asc()) .limit(1) .fetchOne(); } @Override public void updateAccount( Account account, String name, String kind, String externalId, String externalType) { Map<TableField<AccountRecord, String>, String> properties = new HashMap<>(); if (StringUtils.isNotEmpty(name)) { properties.put(ACCOUNT.NAME, name); } if (StringUtils.isNotEmpty(kind)) { properties.put(ACCOUNT.KIND, kind); } if (StringUtils.isNotEmpty(externalId)) { properties.put(ACCOUNT.EXTERNAL_ID, externalId); } if (StringUtils.isNotEmpty(externalType)) { properties.put(ACCOUNT.EXTERNAL_ID_TYPE, externalType); } int updateCount = create().update(ACCOUNT).set(properties).where(ACCOUNT.ID.eq(account.getId())).execute(); if (1 != updateCount) { throw new RuntimeException("UpdateAccount failed."); } } @Override public List<Account> getAccessibleProjects( Set<Identity> identities, boolean isAdmin, Long usingAccount) { List<Account> projects = new ArrayList<>(); if (identities == null) { return projects; } if (isAdmin) { projects.addAll( create() .selectFrom(ACCOUNT) .where(ACCOUNT.KIND.eq(ProjectConstants.TYPE).and(ACCOUNT.REMOVED.isNull())) .orderBy(ACCOUNT.ID.asc()) .fetch()); return projects; } if (usingAccount != null) { Account project = getAccountById(usingAccount); if (project != null && project.getKind().equalsIgnoreCase(ProjectConstants.TYPE)) { projects.add(project); return projects; } } // DSL.falseCondition is created so that we can dynamically build a or // Condition without caring what the external Ids are and still make one // Database call. Condition allMembers = DSL.falseCondition(); for (Identity id : identities) { allMembers = allMembers.or( PROJECT_MEMBER .EXTERNAL_ID .eq(id.getExternalId()) .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(id.getExternalIdType())) .and(PROJECT_MEMBER.REMOVED.isNull()) .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE))); } SelectQuery<Record> query = create().selectQuery(); query.addFrom(ACCOUNT); query.addJoin(PROJECT_MEMBER, PROJECT_MEMBER.PROJECT_ID.equal(ACCOUNT.ID)); query.addConditions(allMembers); query.setDistinct(true); projects.addAll(query.fetchInto(ACCOUNT)); Map<Long, Account> returnProjects = new HashMap<>(); for (Account project : projects) { returnProjects.put(project.getId(), project); } projects = new ArrayList<>(); projects.addAll(returnProjects.values()); return projects; } public List<? extends ProjectMember> getActiveProjectMembers(long id) { return create() .selectFrom(PROJECT_MEMBER) .where(PROJECT_MEMBER.PROJECT_ID.eq(id)) .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE)) .and(PROJECT_MEMBER.REMOVED.isNull()) .orderBy(PROJECT_MEMBER.ID.asc()) .fetch(); } public List<? extends ProjectMember> getProjectMembersByIdentity( long projectId, Set<Identity> identities) { Condition allMembers = DSL.falseCondition(); for (Identity identity : identities) { allMembers = allMembers.or( PROJECT_MEMBER .EXTERNAL_ID .eq(identity.getExternalId()) .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(identity.getExternalIdType())) .and(PROJECT_MEMBER.REMOVED.isNull()) .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE)) .and(PROJECT_MEMBER.PROJECT_ID.eq(projectId))); } SelectQuery<Record> query = create().selectQuery(); query.addFrom(PROJECT_MEMBER); query.addConditions(allMembers); query.setDistinct(true); return query.fetchInto(PROJECT_MEMBER); } @Override public ProjectMember getProjectMember(long id) { return create().selectFrom(PROJECT_MEMBER).where(PROJECT_MEMBER.ID.eq(id)).fetchOne(); } @Override public boolean hasAccessToProject( long projectId, Long usingAccount, boolean isAdmin, Set<Identity> identities) { return isProjectMember(projectId, usingAccount, isAdmin, identities); } @Override public boolean isProjectOwner( long projectId, Long usingAccount, boolean isAdmin, Set<Identity> identities) { if (identities == null) { return false; } if (isAdmin) { return true; } if (usingAccount != null && usingAccount.equals(projectId)) { return false; } Set<ProjectMemberRecord> projectMembers = new HashSet<>(); Condition allMembers = DSL.falseCondition(); for (Identity id : identities) { allMembers = allMembers.or( PROJECT_MEMBER .EXTERNAL_ID .eq(id.getExternalId()) .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(id.getExternalIdType())) .and(PROJECT_MEMBER.ROLE.eq(ProjectConstants.OWNER)) .and(PROJECT_MEMBER.PROJECT_ID.eq(projectId)) .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE)) .and(PROJECT_MEMBER.REMOVED.isNull())); } projectMembers.addAll(create().selectFrom(PROJECT_MEMBER).where(allMembers).fetch()); return !projectMembers.isEmpty(); } public boolean isProjectMember( long projectId, Long usingAccount, boolean isAdmin, Set<Identity> identities) { if (identities == null) { return false; } if ((usingAccount != null && usingAccount.equals(projectId)) || isAdmin) { return true; } Set<ProjectMemberRecord> projectMembers = new HashSet<>(); Condition allMembers = DSL.falseCondition(); for (Identity id : identities) { allMembers = allMembers.or( PROJECT_MEMBER .EXTERNAL_ID .eq(id.getExternalId()) .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(id.getExternalIdType())) .and(PROJECT_MEMBER.PROJECT_ID.eq(projectId)) .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE)) .and(PROJECT_MEMBER.REMOVED.isNull())); } projectMembers.addAll(create().selectFrom(PROJECT_MEMBER).where(allMembers).fetch()); return !projectMembers.isEmpty(); } @Override public List<? extends ProjectMember> setProjectMembers( final Account project, final Set<Member> members) { return lockManager.lock( new ProjectLock(project), new LockCallback<List<? extends ProjectMember>>() { public List<? extends ProjectMember> doWithLock() { List<? extends ProjectMember> previousMembers = getActiveProjectMembers(project.getId()); Set<Member> otherPreviosMembers = new HashSet<>(); for (ProjectMember member : previousMembers) { String projectId = (String) ApiContext.getContext() .getIdFormatter() .formatId(objectManager.getType(Account.class), member.getProjectId()); otherPreviosMembers.add(new Member(member, projectId)); } Set<Member> create = new HashSet<Member>(members); Set<Member> delete = new HashSet<Member>(otherPreviosMembers); for (Member member : members) { if (delete.remove(member)) { create.remove(member); } } Condition allMembers = DSL.falseCondition(); for (Member member : delete) { allMembers = allMembers.or( PROJECT_MEMBER .EXTERNAL_ID .eq(member.getExternalId()) .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(member.getExternalIdType())) .and(PROJECT_MEMBER.PROJECT_ID.eq(project.getId())) .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE))); } List<? extends ProjectMember> toDelete = create().selectFrom(PROJECT_MEMBER).where(allMembers).fetch(); for (ProjectMember member : toDelete) { objectProcessManager.executeStandardProcess(StandardProcess.DEACTIVATE, member, null); objectProcessManager.executeStandardProcess(StandardProcess.REMOVE, member, null); } List<ProjectMember> newMembers = new ArrayList<>(); for (Member member : create) { newMembers.add(createProjectMember(project, member)); } return getActiveProjectMembers(project.getId()); } }); } public ProjectMember createProjectMember(Account project, Member member) { Map<Object, Object> properties = new HashMap<>(); properties.put(PROJECT_MEMBER.PROJECT_ID, project.getId()); properties.put(PROJECT_MEMBER.ACCOUNT_ID, project.getId()); properties.put(PROJECT_MEMBER.NAME, member.getName()); properties.put(PROJECT_MEMBER.EXTERNAL_ID, member.getExternalId()); properties.put(PROJECT_MEMBER.EXTERNAL_ID_TYPE, member.getExternalIdType()); properties.put(PROJECT_MEMBER.ROLE, member.getRole()); return resourceDao.create( ProjectMember.class, objectManager.convertToPropertiesFor(ProjectMember.class, properties)); } @Override public void ensureAllProjectsHaveNonRancherIdMembers(Identity identity) { // This operation is expensive if there are alot of projects and members however this is // only called when auth is being turned on. In most cases this will only be called once. Member newMember = new Member(identity, ProjectConstants.OWNER); Set<Identity> identities = new HashSet<>(); Account account = getAdminAccount(); identities.add(new Identity(ProjectConstants.RANCHER_ID, String.valueOf(account.getId()))); List<Account> allProjects = getAccessibleProjects(identities, true, null); for (Account project : allProjects) { List<? extends ProjectMember> members = getActiveProjectMembers(project.getId()); boolean hasNonRancherMember = false; for (ProjectMember member : members) { if (!member.getExternalIdType().equalsIgnoreCase(ProjectConstants.RANCHER_ID)) { hasNonRancherMember = true; } else if (member.getExternalId().equals(String.valueOf(getAdminAccount().getId()))) { deactivateThenRemove(member); } else { hasNonRancherMember = true; } } if (!hasNonRancherMember) { createProjectMember(project, newMember); } } } private void deactivateThenRemove(ProjectMember member) { Object state = ObjectUtils.getPropertyIgnoreErrors(member, ObjectMetaDataManager.STATE_FIELD); if (CommonStatesConstants.ACTIVE.equals(state)) { objectProcessManager.executeStandardProcess(StandardProcess.DEACTIVATE, member, null); member = objectManager.reload(member); } if (CommonStatesConstants.PURGED.equals(state)) { return; } objectProcessManager.executeStandardProcess(StandardProcess.REMOVE, member, null); } @Override public boolean hasAccessToAnyProject( Set<Identity> identities, boolean isAdmin, Long usingAccount) { return !getAccessibleProjects(identities, isAdmin, usingAccount).isEmpty(); } }
public class RedisEventingService extends AbstractThreadPoolingEventService implements InitializationTask { private static final DynamicStringProperty REDIS_HOST = ArchaiusUtil.getString("redis.hosts"); volatile List<RedisConnection> connections = new ArrayList<RedisConnection>(); int index = 0; public void reconnect() { List<RedisConnection> newConnections = new ArrayList<RedisConnection>(); for (String host : REDIS_HOST.get().trim().split("\\s*,\\s*")) { String[] parts = host.split(":"); if (parts.length > 2) { throw new IllegalArgumentException( "Invalid redis host [" + host + "] should be in host:port format"); } String hostName = parts[0]; int port = Protocol.DEFAULT_PORT; if (parts.length > 1) { try { port = Integer.parseInt(parts[1]); } catch (NumberFormatException e) { throw new IllegalArgumentException( "Invalid redis host [" + host + "] should be in host:port format", e); } } newConnections.add(new RedisConnection(this, hostName, port)); } List<RedisConnection> oldConnections = connections; connections = newConnections; for (RedisConnection conn : newConnections) { getExecutorService().submit(conn); } for (RedisConnection conn : oldConnections) { conn.stop(); } } @Override public void start() { super.start(); reconnect(); REDIS_HOST.addCallback( new Runnable() { @Override public void run() { reconnect(); } }); } @Override public void stop() { super.stop(); for (RedisConnection conn : connections) { conn.stop(); } } protected void onMessage(String pattern, String channel, String message) { onEvent(pattern, channel, message); } @Override protected boolean doPublish(String name, Event event, String eventString) throws IOException { if (connections.size() == 0) { return false; } RedisConnection conn = null; try { conn = connections.get(index); } catch (IndexOutOfBoundsException e) { if (connections.size() > 0) { conn = connections.get(0); } } index = (index + 1) % connections.size(); if (conn != null) { return conn.publish(name, eventString); } return false; } @Override protected void doSubscribe(String eventName, final SettableFuture<?> future) { List<SettableFuture<?>> futures = new ArrayList<SettableFuture<?>>(); for (RedisConnection conn : connections) { SettableFuture<?> newFuture = SettableFuture.create(); conn.subscribe(eventName, newFuture); futures.add(newFuture); } final ListenableFuture<?> combined = Futures.allAsList(futures); combined.addListener( new Runnable() { @Override public void run() { try { combined.get(); future.set(null); } catch (InterruptedException e) { future.setException(e); } catch (ExecutionException e) { future.setException(e.getCause()); } } }, getExecutorService()); } @Override protected void doUnsubscribe(String eventName) { for (RedisConnection conn : connections) { conn.unsubscribe(eventName); } } @Override protected void disconnect() { for (RedisConnection conn : connections) { conn.disconnect(); } } }
public class SubSchemaFactory extends AbstractSchemaFactory implements SchemaFactory { private static DynamicBooleanProperty SERIALIZE = ArchaiusUtil.getBoolean("serialize.schemas"); SchemaFactory schemaFactory; String id; Map<String, Schema> schemaMap; List<SchemaImpl> schemaList = new ArrayList<SchemaImpl>(); List<SchemaPostProcessor> postProcessors = new ArrayList<SchemaPostProcessor>(); boolean init = false; public synchronized void init() { if (init) { return; } schemaMap = new HashMap<String, Schema>(); if (schemaFactory instanceof SubSchemaFactory) { ((SubSchemaFactory) schemaFactory).init(); } List<SchemaImpl> result = new ArrayList<SchemaImpl>(); for (Schema schema : schemaFactory.listSchemas()) { if (schema instanceof SchemaImpl) { /* Copy */ SchemaImpl impl = new SchemaImpl((SchemaImpl) schema); for (SchemaPostProcessor post : postProcessors) { impl = post.postProcessRegister(impl, this); if (impl == null) { break; } } if (impl != null) { result.add(impl); schemaMap.put(impl.getId(), impl); } } } schemaList = result; for (SchemaImpl schema : schemaList) { for (SchemaPostProcessor post : postProcessors) { schema = post.postProcess(schema, this); } } for (SchemaImpl schema : schemaList) { prune(schema); } if (SERIALIZE.get()) { serializeSchema(listSchemas(), getId()); synchronized (schemaFactory) { serializeSchema(schemaFactory.listSchemas(), schemaFactory.getId()); } } init = true; } protected static void serializeSchema(List<Schema> schemaList, String id) { try (FileOutputStream fos = new FileOutputStream(new File(id + ".ser"))) { ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(schemaList); oos.close(); } catch (IOException e) { throw new IllegalStateException(e); } } protected void prune(SchemaImpl schema) { Map<String, Field> fields = schema.getResourceFields(); Map<String, Filter> filters = schema.getCollectionFilters(); for (String name : new HashSet<String>(fields.keySet())) { Field field = fields.get(name); List<FieldType> subTypeEnums = field.getSubTypeEnums(); List<String> subTypes = field.getSubTypes(); for (int i = 0; i < subTypeEnums.size(); i++) { if (subTypeEnums.get(i) == FieldType.TYPE && !schemaMap.containsKey(subTypes.get(i)) && !"type".equals(subTypes.get(i))) { fields.remove(name); filters.remove(name); break; } } } Iterator<String> childrenIter = schema.getChildren().iterator(); while (childrenIter.hasNext()) { if (!schemaMap.containsKey(childrenIter.next())) { childrenIter.remove(); } } } @Override public String getId() { return id; } @SuppressWarnings({"unchecked", "rawtypes"}) @Override public List<Schema> listSchemas() { return (List) schemaList; } @Override public Schema getSchema(Class<?> clz) { return getSchema(schemaFactory.getSchemaName(clz)); } @Override public Schema getSchema(String type) { Schema parentSchema = schemaFactory.getSchema(type); return parentSchema == null ? null : schemaMap.get(parentSchema.getId()); } @Override public Class<?> getSchemaClass(String type) { Schema schema = getSchema(type); return schema == null ? null : schemaFactory.getSchemaClass(type); } @Override public Schema registerSchema(Object obj) { throw new UnsupportedOperationException(); } @Override public Schema parseSchema(String name) { throw new UnsupportedOperationException(); } public SchemaFactory getSchemaFactory() { return schemaFactory; } public List<SchemaPostProcessor> getPostProcessors() { return postProcessors; } public void setPostProcessors(List<SchemaPostProcessor> postProcessors) { this.postProcessors = postProcessors; } public void setId(String id) { this.id = id; } public void setSchemaFactory(SchemaFactory schemaFactory) { this.schemaFactory = schemaFactory; } }