/*
 * Copyright 2015 Red Hat, Inc.
 *
 *  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 io.vertx.ext.auth.jwt.impl;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.auth.AbstractUser;
import io.vertx.ext.auth.AuthProvider;

import java.nio.charset.StandardCharsets;

/**
 * @author Paulo Lopes
 */
public class JWTUser extends AbstractUser {

  private static final Logger log = LoggerFactory.getLogger(JWTUser.class);

  private JsonObject jwtToken;
  private JsonArray permissions;

  public JWTUser() {
    // required if the object is serialized, however this is not a good idea
    // because JWT are supposed to be used in stateless environments
    log.info("You are probably serializing the JWT User, JWT are supposed to be used in stateless servers!");
  }

  public JWTUser(JsonObject jwtToken, String permissionsClaimKey) {
    this.jwtToken = jwtToken;

    if(permissionsClaimKey.contains("/")) {
      getNestedJsonValue(jwtToken, permissionsClaimKey);
    } else {
      this.permissions = jwtToken.getJsonArray(permissionsClaimKey, null);
    }

  }

  private void getNestedJsonValue(JsonObject jwtToken, String permissionsClaimKey) {
    String[] keys = permissionsClaimKey.split("/");
    JsonObject obj = null;
    for(int i = 0; i < keys.length; i++) {
        if(i == 0) {
          obj = jwtToken.getJsonObject(keys[i]);
        } else if (i == keys.length -1) {
          if(obj != null) {
            this.permissions = obj.getJsonArray(keys[i]);
          }
        } else {
            obj = obj.getJsonObject(keys[i]);
        }
    }
  }

  @Override
  public JsonObject principal() {
    return jwtToken;
  }

  @Override
  public void setAuthProvider(AuthProvider authProvider) {
    // NOOP - JWT tokens are self contained :)
  }

  @Override
  public void doIsPermitted(String permission, Handler<AsyncResult<Boolean>> handler) {
    if (permissions != null) {
      for (Object jwtPermission : permissions) {
        if (permission.equals(jwtPermission)) {
          handler.handle(Future.succeededFuture(true));
          return;
        }
      }
    }

    log.debug("User has no permission [" + permission + "]");
    handler.handle(Future.succeededFuture(false));
  }

  @Override
  public void writeToBuffer(Buffer buff) {
    super.writeToBuffer(buff);
    byte[] bytes = jwtToken.encode().getBytes(StandardCharsets.UTF_8);
    buff.appendInt(bytes.length);
    buff.appendBytes(bytes);

    bytes = permissions.encode().getBytes(StandardCharsets.UTF_8);
    buff.appendInt(bytes.length);
    buff.appendBytes(bytes);
  }

  @Override
  public int readFromBuffer(int pos, Buffer buffer) {
    pos = super.readFromBuffer(pos, buffer);
    int len = buffer.getInt(pos);
    pos += 4;
    byte[] bytes = buffer.getBytes(pos, pos + len);
    jwtToken = new JsonObject(new String(bytes, StandardCharsets.UTF_8));
    pos += len;

    len = buffer.getInt(pos);
    pos += 4;
    bytes = buffer.getBytes(pos, pos + len);
    permissions = new JsonArray(new String(bytes, StandardCharsets.UTF_8));
    pos += len;

    return pos;
  }
}

@@ -2192,24 +2192,52 @@
    %7D else %7B%0A
+          if(obj != null) %7B%0A
             
@@ -2262,32 +2262,44 @@
 bject(keys%5Bi%5D);%0A
+          %7D%0A
         %7D%0A    %7D%0A
@@ -3163,21 +3163,53 @@
 bytes);%0A
+    if (permissions != null) %7B
 %0A    
+  
 bytes = 
@@ -3259,32 +3259,34 @@
 ets.UTF_8);%0A    
+  
 buff.appendInt(b
@@ -3295,32 +3295,34 @@
 es.length);%0A    
+ 
+ 
 buff.appendBytes
@@ -3330,16 +3330,60 @@
 bytes);%0A
+    %7D else %7B%0A      buff.appendInt(0);%0A    %7D%0A
   %7D%0A%0A  @
@@ -3717,32 +3717,53 @@
 ;%0A    pos += 4;%0A
+    if (len %3E 0) %7B%0A  
     bytes = buff
@@ -3787,24 +3787,26 @@
 pos + len);%0A
+  
     permissi
@@ -3865,32 +3865,34 @@
 ts.UTF_8));%0A    
+  
 pos += len;%0A%0A   
@@ -3883,24 +3883,29 @@
 pos += len;%0A
+    %7D
 %0A    return 

Fix #107