001/*
002 * Copyright 2012 Atteo.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.atteo.moonshine.shiro;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.List;
021
022import javax.inject.Inject;
023import javax.servlet.FilterChain;
024import javax.servlet.ServletRequest;
025import javax.servlet.ServletResponse;
026import javax.xml.bind.annotation.XmlElement;
027import javax.xml.bind.annotation.XmlElementRef;
028import javax.xml.bind.annotation.XmlElementWrapper;
029import javax.xml.bind.annotation.XmlRootElement;
030
031import org.apache.shiro.SecurityUtils;
032import org.apache.shiro.authz.annotation.RequiresGuest;
033import org.apache.shiro.authz.annotation.RequiresPermissions;
034import org.apache.shiro.authz.annotation.RequiresUser;
035import org.apache.shiro.guice.ShiroModule;
036import org.apache.shiro.guice.aop.ShiroAopModule;
037import org.apache.shiro.guice.web.GuiceShiroFilter;
038import org.apache.shiro.guice.web.ShiroWebModule;
039import org.apache.shiro.mgt.SecurityManager;
040import org.apache.shiro.realm.Realm;
041import org.apache.shiro.session.mgt.SessionManager;
042import org.apache.shiro.web.filter.mgt.FilterChainResolver;
043import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
044import org.apache.shiro.web.mgt.WebSecurityManager;
045import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
046import org.atteo.evo.config.XmlDefaultValue;
047import org.atteo.moonshine.services.Service;
048import org.atteo.moonshine.TopLevelService;
049
050import com.google.inject.Key;
051import com.google.inject.Module;
052import com.google.inject.PrivateModule;
053import com.google.inject.binder.AnnotatedBindingBuilder;
054import com.google.inject.multibindings.Multibinder;
055import com.google.inject.name.Names;
056
057/**
058 * ShiroService service.
059 *
060 * <p>
061 * Binds {@link SecurityManager}.
062 * </p>
063 */
064@XmlRootElement(name = "shiro")
065public class ShiroService extends TopLevelService {
066
067    /**
068     * Enables Shiro AOP functionality.
069     *
070     * <p>
071     * With Shiro AOP you can use annotations for permission checking:
072     * {@link RequiresPermissions}, {@link RequiresUser}, {@link RequiresGuest}, etc.
073     * </p>
074     */
075    @XmlElement
076    @XmlDefaultValue("true")
077    private Boolean aop;
078
079    @XmlElementWrapper(name = "realms")
080    @XmlElementRef
081    private List<RealmService> realms = new ArrayList<>();
082
083    /**
084     * URL prefix to filter through ShiroService.
085     */
086    @XmlElement
087    private String prefix = "/*";
088
089    @Override
090    public Iterable<? extends Service> getSubServices() {
091        return realms;
092    }
093
094    @Override
095    public Module configure() {
096        return new PrivateModule() {
097            @Override
098            protected void configure() {
099                install(new ShiroModule() {
100                    @Override
101                    protected void configureShiro() {
102                        Multibinder<Realm> setBinder = Multibinder.newSetBinder(binder(), Realm.class);
103                        for (RealmService realm : realms) {
104                            if (realm.getId() == null) {
105                                setBinder.addBinding().to(Realm.class);
106                            } else {
107                                setBinder.addBinding().to(Key.get(Realm.class, Names.named(realm.getId())));
108                            }
109
110                        }
111
112                        try {
113                            // Guice will initialize manager with list of realms
114                            bind(WebSecurityManager.class).toConstructor(
115                                    DefaultWebSecurityManager.class.getConstructor(Collection.class))
116                                    .asEagerSingleton();
117                        } catch (NoSuchMethodException e) {
118                            addError(e);
119                        }
120                        expose(WebSecurityManager.class);
121                    }
122
123                    @Override
124                    protected void bindSessionManager(AnnotatedBindingBuilder<SessionManager> bind) {
125                        // make configurable
126                        bind.to(DefaultWebSessionManager.class).asEagerSingleton();
127                    }
128                });
129                FilterChainResolver filterChainResolver = new FilterChainResolver() {
130                    @Override
131                    public FilterChain getChain(ServletRequest request, ServletResponse response,
132                            FilterChain chain) {
133                        return null;
134                    }
135                };
136                bind(FilterChainResolver.class).toInstance(filterChainResolver);
137
138                bind(GuiceShiroFilter.class).asEagerSingleton();
139
140                install(ShiroWebModule.guiceFilterModule(prefix));
141                if (aop) {
142                    install(new ShiroAopModule());
143                }
144
145                expose(SecurityManager.class);
146                expose(WebSecurityManager.class);
147            }
148        };
149    }
150
151    @Inject
152    private SecurityManager securityManager;
153
154    @Override
155    public void start() {
156        SecurityUtils.setSecurityManager(securityManager);
157    }
158
159    @Override
160    public void close() {
161        SecurityUtils.setSecurityManager(null);
162    }
163}