-
Notifications
You must be signed in to change notification settings - Fork 155
351 tenant resolver #896
base: master
Are you sure you want to change the base?
351 tenant resolver #896
Changes from 6 commits
0071aba
f9daa5c
88c45a6
2a4ede9
bdd56de
85c8d4b
ec34cb7
7d10e79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| /* | ||
| * Copyright 2016 Stormpath, Inc. | ||
| * | ||
| * 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. | ||
| */ | ||
| package com.stormpath.sdk.servlet.tenant; | ||
|
|
||
| import com.stormpath.sdk.accountStoreMapping.AccountStoreMapping; | ||
| import com.stormpath.sdk.application.Application; | ||
| import com.stormpath.sdk.application.ApplicationAccountStoreMappingList; | ||
| import com.stormpath.sdk.directory.AccountStore; | ||
| import com.stormpath.sdk.directory.AccountStoreVisitor; | ||
| import com.stormpath.sdk.directory.Directory; | ||
| import com.stormpath.sdk.group.Group; | ||
| import com.stormpath.sdk.organization.Organization; | ||
| import com.stormpath.sdk.servlet.application.ApplicationResolver; | ||
| import com.stormpath.sdk.servlet.http.Resolver; | ||
|
|
||
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| /** | ||
| * @since 1.0.0 | ||
| */ | ||
| public class DefaultTenantResolver implements TenantResolver<Organization> { | ||
|
|
||
| Resolver<String> organizationNameKeyResolver; | ||
| ApplicationResolver applicationResolver = ApplicationResolver.INSTANCE; | ||
|
|
||
| public void setOrganizationNameKeyResolver(Resolver<String> organizationNameKeyResolver) { | ||
| this.organizationNameKeyResolver = organizationNameKeyResolver; | ||
| } | ||
|
|
||
| public Organization get(HttpServletRequest request, HttpServletResponse response) { | ||
|
|
||
| final String domainName = organizationNameKeyResolver.get(request, response); | ||
| if (domainName == null) { | ||
| return null; | ||
| } | ||
|
|
||
| Application application = applicationResolver.getApplication(request); | ||
| ApplicationAccountStoreMappingList accountStoreMappings = application.getAccountStoreMappings(); | ||
| final Organization organization[] = {null}; | ||
| for (AccountStoreMapping accountStoreMapping : accountStoreMappings) { | ||
| final AccountStore accountStore = accountStoreMapping.getAccountStore(); | ||
|
|
||
| accountStore.accept(new AccountStoreVisitor() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you use |
||
| @Override | ||
| public void visit(Group group) { | ||
| //no-op | ||
| } | ||
|
|
||
| @Override | ||
| public void visit(Directory directory) { | ||
| //no-op | ||
| } | ||
|
|
||
| @Override | ||
| public void visit(Organization org) { | ||
| if (domainName.equals(org.getName())) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be: |
||
| organization[0] = org; | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| if (organization[0] != null) break; | ||
| } | ||
| return organization[0]; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| /* | ||
| * Copyright 2016 Stormpath, Inc. | ||
| * | ||
| * 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. | ||
| */ | ||
| package com.stormpath.sdk.servlet.tenant; | ||
|
|
||
| import com.stormpath.sdk.organization.Organization; | ||
| import com.stormpath.sdk.servlet.config.ConfigSingletonFactory; | ||
| import com.stormpath.sdk.servlet.http.Resolver; | ||
| import com.stormpath.sdk.servlet.organization.DefaultOrganizationNameKeyResolver; | ||
|
|
||
| import javax.servlet.ServletContext; | ||
|
|
||
| /** | ||
| * @since 1.0.0 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1.1.0 |
||
| */ | ||
| public class DefaultTenantResolverFactory extends ConfigSingletonFactory<Resolver<Organization>> { | ||
|
|
||
| protected Resolver<Organization> createInstance(ServletContext servletContext) throws Exception { | ||
| DefaultTenantResolver defaultTenantResolver = new DefaultTenantResolver(); | ||
| DefaultOrganizationNameKeyResolver organizationNameKeyResolver = new DefaultOrganizationNameKeyResolver(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't you need to call |
||
| defaultTenantResolver.setOrganizationNameKeyResolver(organizationNameKeyResolver); | ||
| return defaultTenantResolver; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /* | ||
| * Copyright 2016 Stormpath, Inc. | ||
| * | ||
| * 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. | ||
| */ | ||
| package com.stormpath.sdk.servlet.tenant; | ||
|
|
||
| import com.stormpath.sdk.servlet.http.Resolver; | ||
|
|
||
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
|
|
||
| /** | ||
| * @since 1.0.0 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1.1.0 |
||
| */ | ||
| public interface TenantResolver<T extends com.stormpath.sdk.resource.Resource> extends Resolver<T> { | ||
|
|
||
| T get(HttpServletRequest request, HttpServletResponse response); | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| /* | ||
| * Copyright 2016 Stormpath, Inc. | ||
| * | ||
| * 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. | ||
| */ | ||
| package com.stormpath.sdk.servlet.tenant | ||
|
|
||
| import com.stormpath.sdk.accountStoreMapping.AccountStoreMapping | ||
| import com.stormpath.sdk.application.Application | ||
| import com.stormpath.sdk.application.ApplicationAccountStoreMappingList | ||
| import com.stormpath.sdk.directory.AccountStoreVisitor | ||
| import com.stormpath.sdk.directory.Directory | ||
| import com.stormpath.sdk.group.Group | ||
| import com.stormpath.sdk.organization.Organization | ||
| import com.stormpath.sdk.servlet.organization.DefaultOrganizationNameKeyResolver | ||
| import com.stormpath.sdk.servlet.util.SubdomainResolver | ||
| import org.easymock.Capture | ||
| import org.easymock.IAnswer | ||
| import org.testng.annotations.Test | ||
|
|
||
| import javax.servlet.http.HttpServletRequest | ||
|
|
||
| import static org.easymock.EasyMock.* | ||
| import static org.testng.Assert.assertEquals | ||
|
|
||
| /** | ||
| * @since 1.0.0 | ||
| */ | ||
| class DefaultTenantResolverTest { | ||
|
|
||
| @Test | ||
| void testOrganizationExistInAccountStores() { | ||
| def request = createMock(HttpServletRequest) | ||
| def application = createStrictMock(Application) | ||
| def applicationAccountStoreMappingList = createStrictMock(ApplicationAccountStoreMappingList) | ||
| def iterator = createMock(Iterator) | ||
| def accountStoreMapping = createMock(AccountStoreMapping) | ||
| def dir = createMock(Directory) | ||
| def organization = createMock(Organization) | ||
|
|
||
| expect(request.getAttribute(Application.getCanonicalName())).andReturn(application) | ||
| expect(request.getHeader(eq('Host'))).andStubReturn('bar.foo.com') | ||
| expect(application.getAccountStoreMappings()).andReturn(applicationAccountStoreMappingList) | ||
| expect(applicationAccountStoreMappingList.iterator()).andReturn(iterator) | ||
| expect(iterator.hasNext()).andReturn(true) | ||
| expect(iterator.next()).andReturn(accountStoreMapping) | ||
| expect(accountStoreMapping.getAccountStore()).andReturn(dir) | ||
| expect(dir.accept(anyObject())).andVoid() | ||
| expect(iterator.hasNext()).andReturn(true) | ||
| expect(iterator.next()).andReturn(accountStoreMapping) | ||
| expect(accountStoreMapping.getAccountStore()).andReturn(organization) | ||
|
|
||
| Capture capturedArgument = new Capture<AccountStoreVisitor>(); | ||
| expect(organization.accept(and(capture(capturedArgument), isA(AccountStoreVisitor)))).andAnswer( | ||
| new IAnswer<AccountStoreVisitor>() { | ||
| @Override | ||
| public AccountStoreVisitor answer() throws Throwable { | ||
| AccountStoreVisitor accountStoreVisitor = (AccountStoreVisitor) capturedArgument.getValue(); | ||
| accountStoreVisitor.visit(organization); | ||
| } | ||
| } | ||
| ) | ||
| expect(organization.getName()).andReturn("bar") //we return "bar" as the organization name found in account store mappings | ||
|
|
||
| replay(request, application, applicationAccountStoreMappingList, iterator, accountStoreMapping, dir, organization) | ||
|
|
||
| def organizationNameKeyResolver = new DefaultOrganizationNameKeyResolver(); | ||
| organizationNameKeyResolver.setSubdomainResolver(new SubdomainResolver()) | ||
| def resolver = new DefaultTenantResolver(); | ||
| resolver.setOrganizationNameKeyResolver(organizationNameKeyResolver) | ||
|
|
||
| assertEquals(resolver.get(request, null), organization) //expected Organization is 'organization' | ||
|
|
||
| verify(request, application, applicationAccountStoreMappingList, iterator, accountStoreMapping, dir, organization) | ||
| } | ||
|
|
||
| @Test | ||
| void testOrganizationDoesNotExistInAccountStores() { | ||
| def request = createMock(HttpServletRequest) | ||
| def application = createStrictMock(Application) | ||
| def applicationAccountStoreMappingList = createStrictMock(ApplicationAccountStoreMappingList) | ||
| def iterator = createMock(Iterator) | ||
| def accountStoreMapping = createMock(AccountStoreMapping) | ||
| def group = createMock(Group) | ||
|
|
||
| expect(request.getAttribute(Application.getCanonicalName())).andReturn(application) | ||
| expect(request.getHeader(eq('Host'))).andStubReturn('bar.foo.com') | ||
| expect(application.getAccountStoreMappings()).andReturn(applicationAccountStoreMappingList) | ||
| expect(applicationAccountStoreMappingList.iterator()).andReturn(iterator) | ||
| expect(iterator.hasNext()).andReturn(true) | ||
| expect(iterator.next()).andReturn(accountStoreMapping) | ||
| expect(accountStoreMapping.getAccountStore()).andReturn(group) | ||
| expect(group.accept(anyObject())).andVoid() | ||
| expect(iterator.hasNext()).andReturn(false) | ||
|
|
||
| replay(request, application, applicationAccountStoreMappingList, iterator, accountStoreMapping, group) | ||
|
|
||
| def organizationNameKeyResolver = new DefaultOrganizationNameKeyResolver(); | ||
| organizationNameKeyResolver.setSubdomainResolver(new SubdomainResolver()) | ||
| def resolver = new DefaultTenantResolver(); | ||
| resolver.setOrganizationNameKeyResolver(organizationNameKeyResolver) | ||
|
|
||
| assertEquals(resolver.get(request, null), null) //expected Organization is null | ||
|
|
||
| verify(request, application, applicationAccountStoreMappingList, iterator, accountStoreMapping, group) | ||
| } | ||
|
|
||
| @Test | ||
| void testNoOrganizationDomain() { | ||
| def request = createMock(HttpServletRequest) | ||
|
|
||
| expect(request.getHeader(eq('Host'))).andStubReturn('foo.com') //no organization found in url | ||
|
|
||
| replay(request) | ||
|
|
||
| def organizationNameKeyResolver = new DefaultOrganizationNameKeyResolver(); | ||
| organizationNameKeyResolver.setSubdomainResolver(new SubdomainResolver()) | ||
| def resolver = new DefaultTenantResolver(); | ||
| resolver.setOrganizationNameKeyResolver(organizationNameKeyResolver) | ||
|
|
||
| assertEquals(resolver.get(request, null), null) | ||
|
|
||
| verify(request) | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1.1.0