i have to intercept APi Publisher events into wso2 ApiManager wso2am-2.1.0.
I wrote an Observer like this..
import java.io.PrintWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.apimgt.api.model.API;
import org.wso2.carbon.apimgt.api.model.APIStatus;
import org.wso2.carbon.apimgt.impl.observers.APIStatusObserver;
public class CustomApiObserver implements APIStatusObserver{
private static final Log log = LogFactory.getLog(CustomApiObserver.class);
#Override
public boolean statusChanged(APIStatus previous, APIStatus current, API api)
{
log.info("API status updated from: " + previous.getStatus() + " to: " +
current.getStatus() + " for the API: " + api.getId().getApiName() + " (" + api.getId().getVersion() + ")");
PrintWriter writer;
try {
writer = new PrintWriter("/opt/CustomApiObserver.log", "UTF-8");
writer.println("The first line");
writer.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
}
packaged as jar and copied into [AM_HOME]/repository/lib and [AM_HOME]/repository/components/lib.
I don't found if the observer should be registered.
There are other configs for enable the Observer?
I googled but I haven't found resources on carbon observers deployments.
wso2server run as root, and i didn't find any logs into carbon dashboard or file wroted, even error traces, as if it doesn't fired.
Thank you all.
Related
Is there any mechanism or method or steps to detect the endpoint(KAA SDK) connectivity to the KAA server from the application.
If no, then how can we identifies failure devices through remotely?? or How can we identifies devices that are not able to communicate with the KAA Server after deploying devices in the field??
How one can achieve this requirement to unlock the power of IOT??
If your endpoint will meet some problems connecting to Kaa server a "failover" will happen.
So you must define your own failover strategy and set it for your Kaa client. Every time failover happens strategy's onFialover() method will be called.
Below you can see the code example for the Java SDK.
import org.kaaproject.kaa.client.DesktopKaaPlatformContext;
import org.kaaproject.kaa.client.Kaa;
import org.kaaproject.kaa.client.KaaClient;
import org.kaaproject.kaa.client.SimpleKaaClientStateListener;
import org.kaaproject.kaa.client.channel.failover.FailoverDecision;
import org.kaaproject.kaa.client.channel.failover.FailoverStatus;
import org.kaaproject.kaa.client.channel.failover.strategies.DefaultFailoverStrategy;
import org.kaaproject.kaa.client.exceptions.KaaRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
* A demo application that shows how to use the Kaa credentials API.
*/
public class CredentialsDemo {
private static final Logger LOG = LoggerFactory.getLogger(CredentialsDemo.class);
private static KaaClient kaaClient;
public static void main(String[] args) throws InterruptedException, IOException {
LOG.info("Demo application started");
try {
// Create a Kaa client and add a startup listener
kaaClient = Kaa.newClient(new DesktopKaaPlatformContext(), new SimpleKaaClientStateListener() {
#Override
public void onStarted() {
super.onStarted();
LOG.info("Kaa client started");
}
}, true);
kaaClient.setFailoverStrategy(new CustomFailoverStrategy());
kaaClient.start();
// ... Do some work ...
LOG.info("Stopping application.");
kaaClient.stop();
} catch (KaaRuntimeException e) {
LOG.info("Cannot connect to server - no credentials found.");
LOG.info("Stopping application.");
}
}
// Give a possibility to manage device behavior when it loses connection
// or has other problems dealing with Kaa server.
private static class CustomFailoverStrategy extends DefaultFailoverStrategy {
#Override
public FailoverDecision onFailover(FailoverStatus failoverStatus) {
LOG.info("Failover happen. Failover type: " + failoverStatus);
// See enum DefaultFailoverStrategy from package org.kaaproject.kaa.client.channel.failover
// to list all possible values
switch (failoverStatus) {
case CURRENT_BOOTSTRAP_SERVER_NA:
LOG.info("Current Bootstrap server is not available. Trying connect to another one.");
// ... Do some recovery, send notification messages, etc. ...
// Trying to connect to another bootstrap node one-by-one every 5 seconds
return new FailoverDecision(FailoverDecision.FailoverAction.USE_NEXT_BOOTSTRAP, 5L, TimeUnit.SECONDS);
default:
return super.onFailover(failoverStatus);
}
}
}
}
UPDATED (2016/10/28)
From the server side you can check endpoint credentials status as shown in method checkCredentialsStatus() in code below. The status IN_USE shows that endpoint has at least one successful connection attempt.
Unfortunately in current Kaa version there are no ways to directly check if endpoint is connected to server or not. I describe them after code example.
package org.kaaproject.kaa.examples.credentials.kaa;
import org.kaaproject.kaa.common.dto.ApplicationDto;
import org.kaaproject.kaa.common.dto.admin.AuthResultDto;
import org.kaaproject.kaa.common.dto.credentials.CredentialsStatus;
import org.kaaproject.kaa.examples.credentials.utils.IOUtils;
import org.kaaproject.kaa.server.common.admin.AdminClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
public class KaaAdminManager {
private static final Logger LOG = LoggerFactory.getLogger(KaaAdminManager.class);
private static final int DEFAULT_KAA_PORT = 8080;
private static final String APPLICATION_NAME = "Credentials demo";
public String tenantAdminUsername = "admin";
public String tenantAdminPassword = "admin123";
private AdminClient adminClient;
public KaaAdminManager(String sandboxIp) {
this.adminClient = new AdminClient(sandboxIp, DEFAULT_KAA_PORT);
}
// ...
/**
* Check credentials status for getting information
* #return credential status
*/
public void checkCredentialsStatus() {
LOG.info("Enter endpoint ID:");
// Reads endpoint ID (aka "endpoint key hash") from user input
String endpointId = IOUtils.getUserInput().trim();
LOG.info("Getting credentials status...");
try {
ApplicationDto app = getApplicationByName(APPLICATION_NAME);
String appToken = app.getApplicationToken();
// CredentialsStatus can be: AVAILABLE, IN_USE, REVOKED
// if endpoint is not found on Kaa server, exception will be thrown
CredentialsStatus status = adminClient.getCredentialsStatus(appToken, endpointId);
LOG.info("Credentials for endpoint ID = {} are now in status: {}", endpointId, status.toString());
} catch (Exception e) {
LOG.error("Get credentials status for endpoint ID = {} failed. Error: {}", endpointId, e.getMessage());
}
}
/**
* Get application object by specified application name
*/
private ApplicationDto getApplicationByName(String applicationName) {
checkAuthorizationAndLogin();
try {
List<ApplicationDto> applications = adminClient.getApplications();
for (ApplicationDto application : applications) {
if (application.getName().trim().equals(applicationName)) {
return application;
}
}
} catch (Exception e) {
LOG.error("Exception has occurred: " + e.getMessage());
}
return null;
}
/**
* Checks authorization and log in
*/
private void checkAuthorizationAndLogin() {
if (!checkAuth()) {
adminClient.login(tenantAdminUsername, tenantAdminPassword);
}
}
/**
* Do authorization check
* #return true if user is authorized, false otherwise
*/
private boolean checkAuth() {
AuthResultDto.Result authResult = null;
try {
authResult = adminClient.checkAuth().getAuthResult();
} catch (Exception e) {
LOG.error("Exception has occurred: " + e.getMessage());
}
return authResult == AuthResultDto.Result.OK;
}
}
You can see an more examples of using AdminClient in class KaaAdminManager in Credentials Demo Application from Kaa sample-apps project on GitHub.
Knowing workarounds
Using Kaa Notifications in conjunction with Kaa Data Collection feature. Server sends specific unicast notification to endpoint (using endpoint ID), then endpoint replies sending data with Data Collection feature. Server wait a bit and checks timestamp of the last appender record (typically in database) for your endpoint (by endpoint ID). All messages go asynchronously, so you must select response-wait time according to your real environment.
Using Kaa Data Collection feature only. This method is simpler but has certain performance drawbacks. You can use it if your endpoints must send data to Kaa server by theirs nature (measuring sensors, etc.). Endpoint just sends data to server at regular intervals. When server needs to check if endpoint is "on-line", it query saved data logs (typically database) to get last record by endpoint ID (key hash) and analyze the timestamp field.
* To make effective use of Kaa Data Collection feature, you must add such metadata in settings of selected Log appender (in Kaa Admin UI): "Endpoint key hash" (the same as "Endpoint ID"), "Timestamp". This will automatically add needed fields to every log record received from endpoints.
I'm new to Kaa myself and unsure whether there is a method to determine that directly in the SDK, but a work-around is that you could have an extra endpoint from which you periodically send an event to all the other endpoints and expect a reply. When an endpoint does not reply, you know there's a problem.
I have a OpenShift Wildfly server. I am building a website with the Spring MVC framework. One of my webpages also uses a WebSocket connection. On the server side, I have used the #ServerEndpoint annotation and javax.websocket.* library to create my websocket:
package com.myapp.spring.web.controller;
import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.springframework.web.socket.server.standard.SpringConfigurator;
#ServerEndpoint(value="/serverendpoint", configurator = SpringConfigurator.class)
public class serverendpoint {
#OnOpen
public void handleOpen () {
System.out.println("JAVA: Client is now connected...");
}
#OnMessage
public String handleMessage (Session session, String message) throws IOException {
if (message.equals("ping")) {
// return "pong"
session.getBasicRemote().sendText("pong");
}
else if (message.equals("close")) {
handleClose();
return null;
}
System.out.println("JAVA: Received from client: "+ message);
MyClass mc = new MyClass(message);
String res = mc.action();
session.getBasicRemote().sendText(res);
return res;
}
#OnClose
public void handleClose() {
System.out.println("JAVA: Client is now disconnected...");
}
#OnError
public void handleError (Throwable t) {
t.printStackTrace();
}
}
OpenShift gives a default URL, so all of my webpages (html files) have the common (canonical) hostname. For the sake of simplicity, I am calling this URL URL A (projectname-domainname.rhclound.com). I created an alias, CNAME, of URL A, which is called URL B (say https://www.mywebsite.tech). URL B is secure, as it has the https.
I am using a JavaScript client to connect to the WebSocket at the path /serverendpoint. The URI I am using in my html webpage file, test.html, is the following:
var wsUri = "wss://" + "projectname-domainname.rhclound.com" + ":8443" + "/serverendpoint";
When I open up URL A (projectname-domainname.rhclound.com/test), the WebSocket connects and everything works fine. However, when I try to connect to the websocket using URL B (https://mywebsite.tech/test), the JavaScript client immediately connects and disconnects.
Here is the message from the console that I receive:
Here is my JavaScript code that connects to the WebSocket:
/****** BEGIN WEBSOCKET ******/
var connectedToWebSocket = false;
var responseMessage = '';
var webSocket = null;
function initWS() {
connectedToWebSocket = false;
var wsUri = "wss://" + "projectname-domainname.rhcloud.com" + ":8443" + "/serverendpoint";
webSocket = new WebSocket(wsUri); // Create a new instance of WebSocket using usUri
webSocket.onopen = function(message) {
processOpen(message);
};
webSocket.onmessage = function(message) {
responseMessage = message.data;
if (responseMessage !== "pong") { // Ping-pong messages to keep a persistent connection between server and client
processResponse(responseMessage);
}
return false;
};
webSocket.onclose = function(message) {
processClose(message);
};
webSocket.onerror = function(message) {
processError(message);
};
console.log("Exiting initWS()");
}
initWS(); //Connect to websocket
function processOpen(message) {
connectedToWebSocket = true;
console.log("JS: Server Connected..."+message);
}
function sendMessage(toServer) { // Send message to server
if (toServer != "close") {
webSocket.send(toServer);
} else {
webSocket.close();
}
}
function processClose(message) {
connectedToWebSocket = false;
console.log("JS: Client disconnected..."+message);
}
function processError(message) {
userInfo("An error occurred. Please contact for assistance", true, true);
}
setInterval(function() {
if (connectedToWebSocket) {
webSocket.send("ping");
}
}, 4000); // Send ping-pong message to server
/****** END WEBSOCKET ******/
After a lot of debugging and trying various things, I concluded that this was problem was occurring because of the Spring Framework. This is because before I introduced the Spring Framework in my project, URL B could connect to the WebSocket, but after introducing Spring, it cannot.
I read on spring's website about WebSocket Policy. I came across their same origin policy which states that an alias, URL B, cannot connect to the WebSocket because it is not the same origin as URL A is. To solve this problem I disabled the same origin policy with WebSockets as said in the documentation, so I added the following code. I thought that doing so would fix my error. Here is what I added:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
#Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
However, this did not fix the problem, so I added the following method to my ApplicationConfig which extends WebMvcConfigurerAdapter:
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("https://www.mywebsite.com");
}
This also didn't work either. Then I tried this:
package com.myapp.spring.security.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
#Configuration
public class MyCorsFilter {
// #Bean
// public FilterRegistrationBean corsFilter() {
// System.out.println("Filchain");
// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// CorsConfiguration config = new CorsConfiguration();
// config.setAllowCredentials(true);
// config.addAllowedOrigin("https://www.mymt.tech");
// config.addAllowedHeader("*");
// config.addAllowedMethod("*");
// source.registerCorsConfiguration("/**", config);
// FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
// bean.setOrder(0);
// System.out.println("Filchain");
// return bean;
// }
#Bean
public CorsFilter corsFilter() {
System.out.println("Filchain");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // you USUALLY want this
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
System.out.println("Filchain");
return new CorsFilter(source);
}
}
This also did not work.
I even changed the var wsURI in the JS code to the following:
var wsUri = "wss://" + "www.mywebsite.com" + ":8443" + "/serverendpoint";
Then var wsUri = "wss://" + "mywebsite.com" + ":8443" + "/serverendpoint";
When I did this, the Google Chrome gave me an error, saying that the handshake failed. However, when I have this URL, var wsUri = "wss://" + "projectname-domianname.rhcloud.com" + ":8443" + "/serverendpoint";, I did not get the error that the handshake didn't occur, but I get a message that the connection opened and closed immediately (as seen above).
So how can I fix this?
Have you tried implementing the WebMvcConfigurer and overriding the method addCorsMappings()? If not try this and see.
#EnableWebMvc
#Configuration
#ComponentScan
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST")
.allowedHeaders("Origin", "Accept", "Content-Type", "Authorization")
.allowCredentials(true)
.maxAge(3600);
}
}
I don't think it's a CORS issue because it's connected successully before being disconnected. If that's CORS, you can't even connect.
I think it's a communication problem between your DNS & openshift because WebSocket need a persistent connection (long-live) which keeps opening between client & server. If your DNS (e.g. CloudFlare or something like that) does not support / not configured to use WebSocket, the client would be disconnected immediately as in your issue.
I got this mail rejecting my new uploaded app on play store
Your app(s) listed at the end of this email use an unsafe
implementation of the interface X509TrustManager. Specifically, the
implementation ignores all SSL certificate validation errors when
establishing an HTTPS connection to a remote host, thereby making your
app vulnerable to man-in-the-middle attacks.
To properly handle SSL certificate validation, change your code in the
checkServerTrusted method of your custom X509TrustManager interface to
raise either CertificateException or IllegalArgumentException whenever
the certificate presented by the server does not meet your
expectations.
While taking a look on the code I found no such method (checkServerTrusted - X509TrustManager). only X509EncodedKeySpec is used for the In-App Billing.
package util;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
public class Security {
private static final String TAG = "IABUtil/Security";
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
return false;
}
PublicKey key = Security.generatePublicKey(base64PublicKey);
return Security.verify(key, signedData, signature);
}
public static PublicKey generatePublicKey(String encodedPublicKey) {
try {
byte[] decodedKey = Base64.decode(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
Log.e(TAG, "Invalid key specification.");
throw new IllegalArgumentException(e);
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
throw new IllegalArgumentException(e);
}
}
public static boolean verify(PublicKey publicKey, String signedData, String signature) {
Signature sig;
try {
sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(Base64.decode(signature))) {
Log.e(TAG, "Signature verification failed.");
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException.");
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key specification.");
} catch (SignatureException e) {
Log.e(TAG, "Signature exception.");
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
}
return false;
}
}
How can this code be modified to fix the above issue? Need help to fix it.
I am trying to create a service to push exchange notifications to asp.net applications, eventually using SignalR.
My plan is to create a notification hub that subscribes each user as they log in to the asp application and listen for notifications for them. As a notification is received the second part of the project is to then use signalR to only send the correct notifications to each user. Once they log out or time out the notification hub will unsubscribe them.
So far I have done a little basic testing and can receive notifications in a little console app for myself with my credentials hard coded. What I am struggling with is how to do this for multiple people simultaneously. For example would I have to create separate threads of this for each user or is there a better way?
I guess regardless I am going to have to use impersonation rather than holding each users credentials right? I'm also going to have to work out a way to auto refresh the timeout for each user if they have an active session.
Below is a little code I found and have been playing with, I would be grateful for any ideas or example anyone could share of how I could best achieve this.
Many thanks
Andy
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Exchange.WebServices.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
service.Url = new Uri("https://server/EWS/Exchange.asmx");
service.Credentials = new NetworkCredential("user", "pass", "domain");
SetStreamingNotifications(service);
while (true)
{ }
}
static void SetStreamingNotifications(ExchangeService service)
{
// Subscribe to streaming notifications on the Inbox folder, and listen
// for "NewMail", "Created", and "Deleted" events.
StreamingSubscription streamingsubscription = service.SubscribeToStreamingNotifications(
new FolderId[] { WellKnownFolderName.Calendar, WellKnownFolderName.Inbox },
EventType.Created,
EventType.Modified);
StreamingSubscriptionConnection connection = new StreamingSubscriptionConnection(service, 9);
connection.AddSubscription(streamingsubscription);
// Delegate event handlers.
connection.OnNotificationEvent +=
new StreamingSubscriptionConnection.NotificationEventDelegate(OnEvent);
connection.OnSubscriptionError +=
new StreamingSubscriptionConnection.SubscriptionErrorDelegate(OnError);
connection.OnDisconnect +=
new StreamingSubscriptionConnection.SubscriptionErrorDelegate(OnDisconnect);
connection.Open();
Console.WriteLine("--------- StreamSubscription event -------");
}
static private void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
{
// Cast the sender as a StreamingSubscriptionConnection object.
StreamingSubscriptionConnection connection = (StreamingSubscriptionConnection)sender;
// Ask the user if they want to reconnect or close the subscription.
ConsoleKeyInfo cki;
Console.WriteLine("The connection to the subscription is disconnected.");
Console.WriteLine("Do you want to reconnect to the subscription? Y/N");
while (true)
{
cki = Console.ReadKey(true);
{
if (cki.Key == ConsoleKey.Y)
{
connection.Open();
Console.WriteLine("Connection open.");
break;
}
else if (cki.Key == ConsoleKey.N)
{
// The ReadKey in the Main() consumes the E.
Console.WriteLine("\n\nPress E to exit");
break;
}
}
}
}
static void OnEvent(object sender, NotificationEventArgs args)
{
StreamingSubscription subscription = args.Subscription;
// Loop through all item-related events.
foreach (NotificationEvent notification in args.Events)
{
switch (notification.EventType)
{
case EventType.NewMail:
Console.WriteLine("\n-------------Mail created:-------------");
break;
case EventType.Created:
Console.WriteLine("\n-------------Item or folder created:-------------");
break;
case EventType.Deleted:
Console.WriteLine("\n-------------Item or folder deleted:-------------");
break;
}
// Display the notification identifier.
if (notification is ItemEvent)
{
// The NotificationEvent for an e-mail message is an ItemEvent.
ItemEvent itemEvent = (ItemEvent)notification;
Console.WriteLine("\nItemId: " + itemEvent.ItemId.UniqueId);
}
else
{
// The NotificationEvent for a folder is an FolderEvent.
//FolderEvent folderEvent = (FolderEvent)notification;
//Console.WriteLine("\nFolderId: " + folderEvent.FolderId.UniqueId);
}
}
}
static void OnError(object sender, SubscriptionErrorEventArgs args)
{
// Handle error conditions.
Exception e = args.Exception;
Console.WriteLine("\n-------------Error ---" + e.Message + "-------------");
}
}
}
The way I solved this problem is by:
Having an account that has the right to impersonate all users.
I create a service for that account with giving a username and
password.
I impersonate a user and add the subscription of the user to the
connection
I create another service which is a close for the main service with
the same username and password, which will impersonate another user
and then add the subscription to the same connection before
Here are two parts of my code . Forget about the LogDevice it is just something internally.
The first part is the detailed impersonation and adding the service to the list of services
the list of services in my case is a dictionary with the userSMTP is the key , the UserSMTP key here is the impersonated account.
/// <summary>
/// Impersonate one user at a time and without using the autodiscovery method to find the proper url for the userSmtp,
/// and copy the provided url to the usersmtp.
/// </summary>
/// <param name="url"> </param>
/// <param name="userSmtp">user smtp</param>
/// <param name="enableTrace">to enable logging from the XML tracing </param>
/// <param name="exchangeVersion">Exchange server version used</param>
private Uri ImpersonateUser(Uri url, string userSmtp, bool enableTrace, ExchangeVersion exchangeVersion)
{
Uri result = url;
var log = "ImpersonateUser \n";
try
{
log += "0/8 Checking services redundancy\n";
if (Services.ContainsKey(userSmtp))
{
Services.Remove(userSmtp);
}
log += "1/8 Create a new service for " + userSmtp + "\n";
var service = new ExchangeService(exchangeVersion);
log += "2/8 Get credentials for the service\n";
var serviceCred = ((System.Net.NetworkCredential)(((WebCredentials)(Services.First().Value.Credentials)).Credentials));
log += "3/8 Assign credentials to the new service\n";
service.Credentials = new WebCredentials(serviceCred.UserName, serviceCred.Password);
log += "4/8 TraceEnabled is" + enableTrace.ToString() + "\n";
service.TraceEnabled = enableTrace;
log += "5/8 Get the Url for the service with AutodiscoverUrl \n";
service.Url = url;
log += "6/8 Assign a new ImpersonatedUserId to the new service for" + userSmtp + "\n";
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, userSmtp);
try
{
log += "7/8 Validating the impersonation\n";
RuleCollection rulecoll = service.GetInboxRules();
}
catch (Exception ex)
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUser: failed to validate the impersonation for {0}\n Exception: {1}\n", userSmtp, ex.Message);
int hr = System.Runtime.InteropServices.Marshal.GetHRForException(ex);
if (hr == -2146233088) // We do not have right to impersonate this user.
{
result = null;
return result;
}
else
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUser(2): trying to resolve {0} with Autodiscover instead...", userSmtp);
result = ImpersonateUser(userSmtp, enableTrace, exchangeVersion);
}
}
log += "8/8 Adding the service \n";
if (!Services.ContainsKey(userSmtp))
{
Services.Add(userSmtp, service);
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUser(2): {0} has been impersonated\n", service.ImpersonatedUserId.Id);
}
}
catch (Exception ex)
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUser(2): exception {0}\n The exception occured after the following steps: \n{1}", ex.Message, log);
}
return result;
}
And here is the code that calls the previous function (i.e. for all users) put in mind that you should somehow storing the email address for every account you want to impersonate.
/// <summary>
/// To Impersonate users in order to get the info from them.
/// </summary>
/// <param name="userSmtps">List of users to be impersonated</param>
/// <param name="enableTrace"> To enable logging from the XML tracing</param>
/// <param name="exchangeVersion">Exchange server version used </param>
public void ImpersonateUsers(ICollection<string> userSmtps)
{
var log = "ImpersonateUsers\n";
var firstUserSmtp = "";
if (userSmtps != null)
if (userSmtps.Count > 0)
{
//the url for the first smtp
try
{
log += "1/2 Impersonating the first userSmtp\n";
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUsers: Getting the Url from the autodiscovery for the first smtp {0} ", userSmtps.First());
bool enableTrace = Services.First().Value.TraceEnabled;
ExchangeVersion exchangeVersion = Services.First().Value.RequestedServerVersion;
firstUserSmtp = userSmtps.First();
var commonSmtpUrl = ImpersonateUser(userSmtps.First(), enableTrace, exchangeVersion);
if (commonSmtpUrl == null) userSmtps.Remove(firstUserSmtp);
// If the list contains other than the first one
log += "2/2 Impersonating " + (userSmtps.Count - 1) + " userSmtps\n";
if (userSmtps.Count >= 1)
{
foreach (var userSmtp in userSmtps)
{
try
{ //skip ther first one because it is already impersonated.
if (userSmtp == firstUserSmtp)
{
continue;
}
// Impersonate the users
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUsers: Impersonating {0} ...", userSmtp);
commonSmtpUrl = ImpersonateUser(userSmtp, enableTrace, exchangeVersion);
}
catch (Exception ex)
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUsers: Impersonating {1}\n exception {0}\n", ex.Message, userSmtp);
}
}
}
}
catch (Exception ex)
{
_logDevice.LogSrvMessage(1, "ExchangeLiteService: ImpersonateUsers: exception {0}\n The exception occured after the following steps: \n{1}", ex.Message, log);
}
}
}
I would have put the subscription part and adding to the connection , but it is a bit ugly and hard to get. but the idea is simply that you should have a connection, and then you go to each service you made and then `connection.AddSubscription(streamingSubscription);
Where streamingSubscription is extracted from the service.
I am having problems authenticating to Google Calendar API v3 using the Java client library (google-api-client version 1.13.2-beta, google-api-services-calendar version v3-rev26-1.13.2-beta).
We have a marketplace app, and installing that app should give it the required credentials for reading (and writing) Google Calendar events.
We are using 2-legged OAuth 1.0, with Hmac, meaning no tokens are necessary.
This is just what I am trying to do, but with Calendar instead of Tasks:
https://developers.google.com/google-apps/help/articles/2lo-in-tasks-for-marketplace
This is the error I'm getting from Google:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError"
} ],
"message" : "Invalid Credentials"
}
We also have customers that don't use the Marketplace app, but have given our domain the appropriate permissions by hand, and everything works fine for them. It is only the customers with the Marketplace app that have this problem (using a different consumerKey and consumerSecret), and that leads me to believe that there is nothing wrong with the code, but we overlooked something somewhere in the Google setup..
The permissions for the Marketplace app look fine.
I have followed every guide I could find, appropriate for our setup (not plenty of them out there though), and as far as I can see I'm doing the right thing.
The API key seems correctly set up (access to the Calendar API is granted). As I mentioned, this works for our customers not coming from the Marketplace app, and they are using the same API key.
Anyway, here's a failing JUnit test to reproduce the error:
import java.io.IOException;
import java.util.List;
import org.junit.Test;
import com.google.api.client.auth.oauth.OAuthHmacSigner;
import com.google.api.client.auth.oauth.OAuthParameters;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarRequest;
import com.google.api.services.calendar.CalendarRequestInitializer;
import com.google.api.services.calendar.model.CalendarListEntry;
import junit.framework.Assert;
public class GoogleOAuthTest {
#Test
public void test() {
final String API_KEY = "...";
final String CONSUMER_KEY = "...";
final String CONSUMER_KEY_SECRET = "...";
final String GOOGLE_USER = "me#mydomain.com";
// The 2-LO authorization section
OAuthHmacSigner signer = new OAuthHmacSigner();
signer.clientSharedSecret = CONSUMER_KEY_SECRET;
final OAuthParameters oauthParameters = new OAuthParameters();
oauthParameters.version = "1";
oauthParameters.consumerKey = CONSUMER_KEY;
oauthParameters.signer = signer;
Calendar service = new Calendar.Builder(new NetHttpTransport(), new JacksonFactory(), oauthParameters)
.setApplicationName("myapp")
.setCalendarRequestInitializer(new CalendarRequestInitializer() {
#Override
protected void initializeCalendarRequest(CalendarRequest<?> request) throws IOException {
request.setKey(API_KEY);
}
}).build();
try {
com.google.api.services.calendar.Calendar.CalendarList.List list = service.calendarList().list();
list.getUnknownKeys().put("xoauth_requestor_id", GOOGLE_USER);
// This is where I get the exception!
List<CalendarListEntry> calendarList = list.execute().getItems();
} catch (IOException e) {
Assert.fail("Test failed: " + e.getMessage());
}
}
}
What could I be doing wrong? Is there something obviously wrong with my code, or is there something we might have missed in the Google setup?
Not sure what the API key is, but I have very similar code except for the request initializer :
CalendarRequestInitializer initializer = new CalendarRequestInitializer() {
#Override
protected void initializeCalendarRequest(CalendarRequest<?> calendarRequest) throws IOException {
ArrayMap<String, Object> customKeys = new ArrayMap<String, Object>();
customKeys.add("xoauth_requestor_id", EMAIL_OF_THE_USER);
calendarRequest.setUnknownKeys(customKeys);
}
};
This works for me... hope it helps;
UPDATE use the API Key that you get when clicking "Register for additional APIs" link in Marketplace -> My Vendor Profile -> My App.