/*
 * Copyright 2012 SURFnet bv, The Netherlands
 *
 * 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 nl.surfnet.coin.selfservice.filter;

import java.io.IOException;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;

import nl.surfnet.coin.api.client.OpenConextOAuthClient;
import nl.surfnet.coin.api.client.domain.Group20;
import nl.surfnet.coin.selfservice.domain.CoinAuthority;
import nl.surfnet.coin.selfservice.domain.CoinUser;
import nl.surfnet.coin.selfservice.util.SpringSecurity;
import nl.surfnet.spring.security.opensaml.SAMLAuthenticationToken;

public class ApiOAuthFilter implements Filter {

  Logger LOG = LoggerFactory.getLogger(ApiOAuthFilter.class);

  private OpenConextOAuthClient apiClient;

  private static final String PROCESSED = "nl.surfnet.coin.selfservice.filter.ApiOAuthFilter.PROCESSED";
  private static final String ORIGINAL_REQUEST_URL = "nl.surfnet.coin.selfservice.filter.ApiOAuthFilter" +
      ".ORIGINAL_REQUEST_URL";
  private String adminTeam;
  private String callbackFlagParameter;

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    final HttpSession session = httpRequest.getSession(true);
    if (SpringSecurity.isFullyAuthenticated() && session.getAttribute(PROCESSED) == null) {
      CoinUser user = SpringSecurity.getCurrentUser();

      if (apiClient.isAccessTokenGranted(user.getUid())) {
        // already authorized before (we have a token)
        elevateUserIfApplicable(user);
        session.setAttribute(PROCESSED, "true");
        LOG.debug("Access token was already granted, processed elevation. Will fall through filter. " +
            "User is now: {}.", user);
      } else if (httpRequest.getParameter(callbackFlagParameter) != null) {
        // callback from OAuth dance, elevate immediately afterwards

        apiClient.oauthCallback(httpRequest, user.getUid());

        elevateUserIfApplicable(user);
        session.setAttribute(PROCESSED, "true");
        LOG.debug("Processed elevation after callback. Will redirect to originally requested location. User is now: " +
            "{}.", user);
        ((HttpServletResponse) response).sendRedirect((String) session.getAttribute(ORIGINAL_REQUEST_URL));
        return;
      } else {
        // No authorization yet, start the OAuth dance
        final String currentRequestUrl = getCurrentRequestUrl(httpRequest);
        session.setAttribute(ORIGINAL_REQUEST_URL, currentRequestUrl);
        LOG.debug("No auth yet, redirecting to auth url: {}", apiClient.getAuthorizationUrl());
        ((HttpServletResponse) response).sendRedirect(apiClient.getAuthorizationUrl());
        return;
      }
    }
    chain.doFilter(request, response);
  }

  private String getCurrentRequestUrl(HttpServletRequest request) {
    StringBuilder sb = new StringBuilder()
        .append(request.getRequestURL());
    if (!StringUtils.isBlank(request.getQueryString())) {
      sb
        .append("?")
        .append(request.getQueryString());
    }
    return sb.toString();
  }


  public void elevateUserIfApplicable(CoinUser coinUser) {
    if (isMemberOfAdminTeam(coinUser)) {
      coinUser.addAuthority(new CoinAuthority("ROLE_ADMIN"));
      SecurityContextHolder.getContext().setAuthentication(
          new SAMLAuthenticationToken(coinUser, "", coinUser.getAuthorities()));
    }
  }

  /**
   * Whether the given coin user is member of the configured admin team
   *
   * @param user the CoinUser
   * @return boolean
   */
  private boolean isMemberOfAdminTeam(CoinUser user) {
    final List<Group20> groups = apiClient.getGroups20(user.getUid(), user.getUid());
    Assert.notNull(groups, "Groups returned from api.surfconext should not be null");

    if (LOG.isDebugEnabled()) {
      LOG.debug("User {} is member of these groups: {}", user.getUid(), groups);
    }
    for (Group20 group : groups) {
      if (group.getId().equals(adminTeam)) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("User {} is member of admin team '{}'", user.getUid(), group.getId());
        }
        return true;
      }
    }
    return false;
  }

  public void setAdminTeam(String adminTeam) {
    this.adminTeam = adminTeam;
  }

  @Override
  public void destroy() {
  }

  public void setApiClient(OpenConextOAuthClient apiClient) {
    this.apiClient = apiClient;
  }

  public void setCallbackFlagParameter(String callbackFlagParameter) {
    this.callbackFlagParameter = callbackFlagParameter;
  }
}

@@ -701,32 +701,8 @@
 on;%0D
-%0Aimport java.util.List;%0D
 %0A%0D%0Ai
@@ -1235,49 +1235,8 @@
 er;%0D
-%0Aimport org.springframework.util.Assert;%0D
 %0A%0D%0Ai
@@ -4869,28 +4869,21 @@
 nal 
-List%3C
 Group20
-%3E
  group
-s
  = a
@@ -4899,17 +4899,16 @@
 getGroup
-s
 20(user.
@@ -4916,16 +4916,27 @@
 etUid(),
+ adminTeam,
  user.ge
@@ -4949,185 +4949,131 @@
 );%0D%0A
-    Assert.notNull(groups, %22Groups returned from api.surfconext should not be null%22);%0D%0A%0D%0A    if (LOG.isDebugEnabled()) %7B%0D%0A      LOG.debug(%22User %7B%7D is member of these groups: %7B%7D%22
+%0D%0A    if (LOG.isDebugEnabled()) %7B%0D%0A      LOG.debug(%22Membership of adminTeam '%7B%7D' for user '%7B%7D': %7B%7D%22, new Object%5B%5D%7BadminTeam
 , us
@@ -5090,17 +5090,17 @@
 ), group
-s
+%7D
 );%0D%0A    
@@ -5110,88 +5110,30 @@
     
-for (Group20 group : groups) %7B%0D%0A      if (group.getId().equals(adminTeam)
+if (group != null
 ) %7B%0D%0A
-  
     
@@ -5159,26 +5159,24 @@
 abled()) %7B%0D%0A
-  
         LOG.
@@ -5259,23 +5259,19 @@
 %0D%0A      
-  
 %7D%0D%0A
-  
       re
@@ -5282,25 +5282,16 @@
  true;%0D%0A
-      %7D%0D%0A
     %7D%0D%0A 

use getGroup() method