001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
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 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.runtime.api.login;
023
024import java.io.IOException;
025import java.io.ObjectInputStream;
026import java.io.ObjectOutputStream;
027import java.io.Serializable;
028import java.util.ArrayList;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032
033import javax.security.auth.Subject;
034import javax.security.auth.callback.CallbackHandler;
035import javax.security.auth.login.AppConfigurationEntry;
036import javax.security.auth.login.LoginContext;
037import javax.security.auth.login.LoginException;
038import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
039
040import org.nuxeo.common.xmap.annotation.XNode;
041import org.nuxeo.common.xmap.annotation.XNodeList;
042import org.nuxeo.common.xmap.annotation.XObject;
043import org.nuxeo.runtime.api.LoginModuleWrapper;
044
045/**
046 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
047 */
048@XObject("domain")
049public class SecurityDomain implements Serializable {
050
051    private static final long serialVersionUID = 5770889355854831093L;
052
053    @XNode("@name")
054    private String name;
055
056    private transient AppConfigurationEntry[] entries;
057
058    public SecurityDomain() {
059    }
060
061    public SecurityDomain(String name) {
062        this.name = name;
063    }
064
065    public SecurityDomain(String name, AppConfigurationEntry[] entries) {
066        this.name = name;
067        this.entries = entries;
068    }
069
070    public String getName() {
071        return name;
072    }
073
074    public AppConfigurationEntry[] getAppConfigurationEntries() {
075        return entries;
076    }
077
078    public void setAppConfigurationEntries(AppConfigurationEntry[] entries) {
079        this.entries = entries;
080    }
081
082    @XNodeList(value = "login-module", type = ArrayList.class, componentType = LoginModuleDescriptor.class)
083    public void setEntries(List<LoginModuleDescriptor> descriptors) {
084        entries = new AppConfigurationEntry[descriptors.size()];
085        int i = 0;
086        for (LoginModuleDescriptor descriptor : descriptors) {
087            LoginModuleControlFlag flag = null;
088            if (descriptor.flag == null) {
089                flag = LoginModuleControlFlag.OPTIONAL;
090            } else if ("optional".equals(descriptor.flag)) {
091                flag = LoginModuleControlFlag.OPTIONAL;
092            } else if ("sufficient".equals(descriptor.flag)) {
093                flag = LoginModuleControlFlag.SUFFICIENT;
094            } else if ("required".equals(descriptor.flag)) {
095                flag = LoginModuleControlFlag.REQUIRED;
096            } else if ("requisite".equals(descriptor.flag)) {
097                flag = LoginModuleControlFlag.REQUISITE;
098            }
099            descriptor.options.put(LoginModuleWrapper.DELEGATE_CLASS_KEY, descriptor.code);
100            entries[i++] = new AppConfigurationEntry(LoginModuleWrapper.class.getName(), flag, descriptor.options);
101        }
102    }
103
104    public LoginContext login(Subject subject) throws LoginException {
105        LoginContext ctx = new LoginContext(name, subject);
106        ctx.login();
107        return ctx;
108    }
109
110    public LoginContext login(CallbackHandler handler) throws LoginException {
111        LoginContext ctx = new LoginContext(name, handler);
112        ctx.login();
113        return ctx;
114    }
115
116    public LoginContext login(Subject subject, CallbackHandler handler) throws LoginException {
117        LoginContext ctx = new LoginContext(name, subject, handler);
118        ctx.login();
119        return ctx;
120    }
121
122    public LoginContext login(String username, Object credentials) throws LoginException {
123        CredentialsCallbackHandler handler = new CredentialsCallbackHandler(username, credentials);
124        LoginContext ctx = new LoginContext(name, handler);
125        ctx.login();
126        return ctx;
127    }
128
129    public static String controlFlagToString(LoginModuleControlFlag flag) {
130        if (flag == LoginModuleControlFlag.OPTIONAL) {
131            return "optional";
132        } else if (flag == LoginModuleControlFlag.REQUIRED) {
133            return "required";
134        } else if (flag == LoginModuleControlFlag.REQUISITE) {
135            return "requisite";
136        } else if (flag == LoginModuleControlFlag.SUFFICIENT) {
137            return "sufficient";
138        }
139        throw new IllegalArgumentException("Not a supported LoginModuleControlFlag: " + flag);
140    }
141
142    public static LoginModuleControlFlag controlFlagFromString(String flag) {
143        if (flag == null) {
144            return LoginModuleControlFlag.OPTIONAL;
145        } else if ("optional".equals(flag)) {
146            return LoginModuleControlFlag.OPTIONAL;
147        } else if ("sufficient".equals(flag)) {
148            return LoginModuleControlFlag.SUFFICIENT;
149        } else if ("required".equals(flag)) {
150            return LoginModuleControlFlag.REQUIRED;
151        } else if ("requisite".equals(flag)) {
152            return LoginModuleControlFlag.REQUISITE;
153        }
154        throw new IllegalArgumentException("Not a supported LoginModuleControlFlag: " + flag);
155    }
156
157    @SuppressWarnings("unchecked")
158    private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
159        in.defaultReadObject();
160        // read app config entries
161        int size = in.readInt();
162        entries = new AppConfigurationEntry[size];
163        for (int i = 0; i < size; i++) {
164            String name = (String) in.readObject();
165            String ctrlFlag = (String) in.readObject();
166            LoginModuleControlFlag flag = controlFlagFromString(ctrlFlag);
167            Map<String, ?> opts = (Map<String, ?>) in.readObject();
168            entries[i] = new AppConfigurationEntry(name, flag, opts);
169        }
170    }
171
172    private void writeObject(ObjectOutputStream out) throws IOException {
173        out.defaultWriteObject();
174        // write app config entries
175        if (entries == null) {
176            out.writeInt(0);
177        } else {
178            out.writeInt(entries.length);
179            for (AppConfigurationEntry entry : entries) {
180                out.writeObject(entry.getLoginModuleName());
181                out.writeObject(controlFlagToString(entry.getControlFlag()));
182                Map<String, ?> opts = entry.getOptions();
183                if (!(opts instanceof Serializable)) {
184                    opts = new HashMap<String, Object>(opts);
185                }
186                out.writeObject(opts);
187            }
188        }
189    }
190
191}