//
//  ========================================================================
//  Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.security;

import java.util.Properties;

import javax.security.auth.Subject;

import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;

public class SpnegoLoginService extends AbstractLifeCycle implements LoginService
{
    private static final Logger LOG = Log.getLogger(SpnegoLoginService.class);

    protected IdentityService _identityService;// = new LdapIdentityService();
    protected String _name;
    private String _config;

    private String _targetName;

    public SpnegoLoginService()
    {

    }

    public SpnegoLoginService( String name )
    {
        setName(name);
    }

    public SpnegoLoginService( String name, String config )
    {
        setName(name);
        setConfig(config);
    }

    @Override
    public String getName()
    {
        return _name;
    }

    public void setName(String name)
    {
        if (isRunning())
        {
            throw new IllegalStateException("Running");
        }

        _name = name;
    }

    public String getConfig()
    {
        return _config;
    }

    public void setConfig( String config )
    {
        if (isRunning())
        {
            throw new IllegalStateException("Running");
        }

        _config = config;
    }



    @Override
    protected void doStart() throws Exception
    {
        Properties properties = new Properties();
        Resource resource = Resource.newResource(_config);
        properties.load(resource.getInputStream());

        _targetName = properties.getProperty("targetName");

        LOG.debug("Target Name {}", _targetName);

        super.doStart();
    }

    /**
     * username will be null since the credentials will contain all the relevant info
     */
    @Override
    public UserIdentity login(String username, Object credentials)
    {
        String encodedAuthToken = (String)credentials;

        byte[] authToken = B64Code.decode(encodedAuthToken);

        GSSManager manager = GSSManager.getInstance();
        try
        {
            Oid krb5Oid = new Oid("1.3.6.1.5.5.2"); // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
            GSSName gssName = manager.createName(_targetName,null);
            GSSCredential serverCreds = manager.createCredential(gssName,GSSCredential.INDEFINITE_LIFETIME,krb5Oid,GSSCredential.ACCEPT_ONLY);
            GSSContext gContext = manager.createContext(serverCreds);

            if (gContext == null)
            {
                LOG.debug("SpnegoUserRealm: failed to establish GSSContext");
            }
            else
            {
                while (!gContext.isEstablished())
                {
                    authToken = gContext.acceptSecContext(authToken,0,authToken.length);
                }
                if (gContext.isEstablished())
                {
                    String clientName = gContext.getSrcName().toString();
                    String role = clientName.substring(clientName.indexOf('@') + 1);

                    LOG.debug("SpnegoUserRealm: established a security context");
                    LOG.debug("Client Principal is: " + gContext.getSrcName());
                    LOG.debug("Server Principal is: " + gContext.getTargName());
                    LOG.debug("Client Default Role: " + role);

                    SpnegoUserPrincipal user = new SpnegoUserPrincipal(clientName,authToken);

                    Subject subject = new Subject();
                    subject.getPrincipals().add(user);

                    return _identityService.newUserIdentity(subject,user, new String[]{role});
                }
            }

        }
        catch (GSSException gsse)
        {
            LOG.warn(gsse);
        }

        return null;
    }

    @Override
    public boolean validate(UserIdentity user)
    {
        return false;
    }

    @Override
    public IdentityService getIdentityService()
    {
        return _identityService;
    }

    @Override
    public void setIdentityService(IdentityService service)
    {
        _identityService = service;
    }

    @Override
    public void logout(UserIdentity user) 
    {
        // TODO Auto-generated method stub
    }

}
