Using the google-gdata client libraries I'm able to add contacs to a Google/GMail account
but when I try to delete them I get two different results based on the type of account.
When the target account is a pure GMail account, I can delete contacts but it does not delete
all contacts in one go. Sometimes it will return a few, sometimes up to 25, even though I'm checking
if there are more contacts and looping over them.
When the target account is a Google Apps account I simply get a timeout (after about 2 minutes)
Note that even for a Google Apps account I can add contacts.
let service = new ContactsService("ContactSync")
service.setUserCredentials(username, password)
let token = service.QueryClientLoginToken()
service.SetAuthenticationToken(token)
let uri = ContactsQuery.CreateContactsUri("default")
let query = new ContactsQuery(uri)
let rec DeleteAllGoogleContacts (query : ContactsQuery) =
let feed = service.Query(query) : ContactsFeed
feed.Entries
|> List.ofSeq
|> List.iter(fun f -> f.Delete())
match feed.NextChunk with
| x when String.IsNullOrWhiteSpace(x) -> ()
| _ -> DeleteAllGoogleContacts(new ContactsQuery(feed.NextChunk))
DeleteAllGoogleContacts(query)
The error for a Google Apps account is (email changed for privacy):
Google.GData.Client.GDataRequestException: Execution of request
failed:
https://www.google.com/m8/feeds/contacts/email#domain.com/full/22280c0f9f627f
---> System.Net.WebException: The request timed out
at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in :0
at System.Net.HttpWebRequest.GetResponse () [0x00000] in :0
at Google.GData.Client.GDataRequest.Execute () [0x00000] in :0
--- End of inner exception stack trace ---
at Google.GData.Client.GDataRequest.Execute () [0x00000] in :0
at Google.GData.Client.GDataGAuthRequest.Execute (Int32 retryCounter) [0x00000] in :0
When DeleteAllGoogleContacts is first called, it will succeed in getting the feed
and the number of entries is smaller or equal to 25. The timeout occurs upon calling f.Delete()
Please note that, as far as I am currently aware, I cannot use OAuth here because
this will run on a headless server with no user interaction.
Update:
I found that there's another way to retrieve the contacts
let Delete() =
let requestSettings = new RequestSettings("ContactSync", username, password)
requestSettings.AutoPaging <- true
let contactsRequest = new ContactsRequest(requestSettings)
let feed = contactsRequest.GetContacts() : Feed<Contact>
feed.Entries
|> List.ofSeq
|> List.iter(fun f -> contactsRequest.Delete(f))
This retrieves all contacts in one go, but I still get a timeout upon Delete()
Update 2:
This may actually be firewall related as I can get it to work
from another network. If so, I'll delete the question as it is not relevant.
Update 3:
It does not seem to be caused by a firewall.
When running this on Windows with .NET everything works as expected.
When running this on Linux with the latest Mono and FSharp compiled from source,
we get the timeout exception.
My only guess here is that it's got something to do with the SSL certificates.
For example, if you want to access the GMail SMTP on Linux you have to issue the following commands:
mozroots --import --ask-remove
certmgr --ssl smtps://smtp.gmail.com:465
Otherwise it will not work. Perhaps I have to do something similar for the GData API's?
Update 4:
Looking at the endpoints for Update() and Delete(), you can see they are one and the same
https://www.google.com/m8/feeds/contacts/userEmail/full/{contactId}
The only difference is the HTTP verb being used: PUT vs UPDATE, and I confirmed
that Update() does work on both platforms for both targets (Windows/Linux), (GMail, Google Apps)
The image below shows the 4 different scenarios for Delete():
Related
Is there a way to uniquely identify a user that has logged on my application based on his device/machine ?
In a lot of cases, the IP is enough, but in case when the client has multiple machines behind a NAT, then the same IP is exposed, so I can't tell them apart. it should have same id irrespective of browser.
for e.g. If the user logs in on his account with computer A, then log in on the same account with computer B that share the same router, I need to get id apart those two devices.
I don't know if this is possible, but it would be life saving if it is.
I was faced with this problem where I wanted to ask for Google Authenticator on sign in but only once for each device used by a user. I used a function to give me a device id based on the hostname, the MAC address, and the IP address. I know the MAC address isn't always reliable so I thought combining the data into one string might be a way round it. Our application is limited to <100 users and most of them access from the office or home so I feel it should be good enough.
I put the IP address function (which gets the IPV4) into a separate function as I check whether they are in office (on 192.168..) or external before checking the device ID. The list of device ID's associated with a user is stored in a SQL table and checked after username/password entry but before log in is completed to decide whether to request a 2FA code.
Here's the code:
dim thisDeviceId as String=GetClientDeviceId()
public Function GetClientDeviceId() As string
Dim mac As String = String.Empty
For Each netInterface In NetworkInterface.GetAllNetworkInterfaces()
If _
netInterface.NetworkInterfaceType = NetworkInterfaceType.Wireless80211 OrElse
netInterface.NetworkInterfaceType = NetworkInterfaceType.Ethernet Then
Dim address = netInterface.GetPhysicalAddress()
mac = BitConverter.ToString(address.GetAddressBytes())
End If
Next
return string.Format("{0}-{1}-{2}",dns.GetHostEntry(HttpContext.current.Request.ServerVariables("REMOTE_ADDR")).HostName, mac,GetClientDeviceIpAddress())
End Function
public Function GetClientDeviceIpAddress() As string
Dim ipv4Address As String = String.Empty
For Each currentIpAddress As IPAddress In Dns.GetHostAddresses(Dns.GetHostName())
If currentIpAddress.AddressFamily.ToString() = System.Net.Sockets.AddressFamily.InterNetwork.ToString() Then
ipv4Address = currentIpAddress.ToString()
Exit For
End If
Next
return ipv4Address
End Function
Even though it's not bulletproof and could be improved upon it might help someone.
I'm using HtmlProvider to web scrape stock company news e.g. https://www.nasdaq.com/symbol/{STOCK_SYMBOL_HERE}/news-headlines but I'm getting an error in this line of code
let [<Literal>] stockNewsUrl = "https://www.nasdaq.com/symbol/AAPL/news-headlines"
let news = new HtmlProvider<stockNewsUrl>()
There is squiggle on the second line and the error was Error FS3033 The type provider 'ProviderImplementation.HtmlProvider' reported an error: Cannot read sample HTML from 'https://www.nasdaq.com/symbol/AAPL/news-headlines': The 'Value'='AAPL,technology' part of the cookie is invalid.
To make an HTTP request to https://www.nasdaq.com/symbol/AAPL/news-headlines, we are required to provide a CookieContainer. Since you are using the FSharp.Data library, I suggest to use its HTTP Utilities:
type Nasdaq = HtmlProvider<"/tmp.html">
let cc = CookieContainer ()
let data =
Http.RequestString ("https://www.nasdaq.com/symbol/AAPL/news-headlines", cookieContainer = cc)
|> Nasdaq.Parse
data.Tables.``Today's Market Activity``.Html
|> printfn "%A"
Of course you have to pre-download the page and save to /tmp.html first.
Small note: if we already have the HTML string (as in our case), we use Nasdaq.Parse; if we have a url, we use Nasdaq.Load.
It looks like this fails because F# Data sends cookies in a format that the Nasdaq service does not like. An easy workaround is to download the page once to have a sample available at compile-time and then download the page at runtime using some other means.
type Nasdaq = HtmlProvider<"c:/temp/nasdaq.html">
let wc = new WebClient()
let downloaded = wc.DownloadString("https://www.nasdaq.com/symbol/AAPL/news-headlines")
let ns = Nasdaq.Load(downloaded)
This works, but there are two issues:
The page dos not contain any tables/lists, so the ns value is not giving you nice static access to anything useful
I get timeout exception when I try to download the data using WebClient, so perhaps that also does not work (but it might just be that I'm behind a proxy or something..)
I am using the embedded YAWS web-server with yaws cookie session.
I first authenticate the user with user-name & password to allow him the entry to the web pages.
My problem is if the user directly opens the internal web page instead of login page he can view it even without the authentication. How to restrict the user that he must have the cookie to view any internal web page.
In chapter 7 of the Yaws PDF documentation there's an example that does exactly what you're asking about. It uses arg rewriting to redirect unauthenticated requests to a login page.
First we configure an arg rewriter module named myapp in the server portion of yaws.conf:
arg_rewrite_mod = myapp
The myapp:arg_rewrite/1 function checks the incoming request via the #arg{} record to look for a specific cookie, and if not found and the request isn't trying to retrieve one of the three resources returned from the login_pages/0 function, it calls do_rewrite/1 to rewrite the request to deliver a login.yaws page instead:
arg_rewrite(Arg) ->
OurCookieName = "myapp_sid"
case check_cookie(Arg, OurCookieName) of
{error, _} ->
do_rewrite(Arg);
{ok, _Session} ->
%% return Arg untouched
Arg
end.
%% these pages must be shippable without a good cookie
login_pages() ->
["/banner.gif", "/login.yaws", "/post_login.yaws"].
do_rewrite(Arg) ->
Req = Arg#arg.req,
{abs_path, Path} = Req#http_request.path,
case lists:member(Path, login_pages()) of
true ->
Arg;
false ->
Arg#arg{req = Req#http_request{path = {abs_path, "/login.yaws"}},
state = Path}
end.
Please see the Yaws PDF documentation for further details.
We implement Solr search in a document manager we use. When a user requests to search for all the documents in a given directory, we are forced to aggregate the docids of the user's selected documents into an array and send a
SolrQueryInList("docid", arrFileNames)
If the number of files in the user's directory is very large, so is the resulting http request. Ergo, we decided to use SolrPostConnection. In our site we implement the PostSolrConnection decorator within the Windsor container, as per M. Scheeffer's instructions:
public SolrSearchRepository(int caseID, string solrServer)
{
this.facility = new SolrNetFacility(solrServer);
this.facility.AddCore(caseID.ToString(), typeof(SolrDocument),
string.Format("{0}/{1}", solrServer, caseID));
this.container = new WindsorContainer();
this.container.Register(Component.For<ISolrConnection>()
.ImplementedBy<PostSolrConnection>()
.DependsOn(Parameter.ForKey("serverUrl").Eq(solrServer)));
this.container.AddFacility(this.facility);
solrOperation = this.container
.Resolve<ISolrOperations<SolrDocument>>(caseID.ToString());
}
However, we are still getting the following error:
{"Message":"An error has occurred.","ExceptionMessage":"The remote server returned an
error: (400) Bad Request.","ExceptionType":"System.Net.WebException","StackTrace":"
at System.Net.HttpWebRequest.GetResponse()\r\n at
HttpWebAdapters.Adapters.HttpWebRequestAdapter.GetResponse()\r\n at
SolrNet.Impl.SolrConnection.GetResponse(IHttpWebRequest request)\r\n at
SolrNet.Impl.SolrConnection.Get(String relativeUrl, IEnumerable`1 parameters)"}
We use Tomcat 7.0 server. The logs are pretty uniformative. Here's the pastebin of what seems to happen around the time I launch a search:
Solr Log
I noticed that the command never reaches the server. Is the PostSolrConnection not implementing post? Note that this query works perfectly with small number of files (i.e. docid's)
Quoting from the WebSharper 2.5 alpah docs the remoting component assumes that:
RPC-callable methods are safe to call from the web by an unauthenticated client.
Is there anyway to secure remote calls so they can only be called from an authenticated client?
One of the samples in the WebSharper website is a chat application that seems to do just that by providing a Login method that returns an authentication token, which is then required to call the other functions:
[<Rpc>]
let Login (user: string) : Option<Auth.Token> =
let s = State.Get()
if s.Users.ContainsKey user then
None
else
// (snip)
user |> Auth.Generate |> Some
[<Rpc>]
let Poll (auth: Auth.Token) (time: int) =
// (snip)
The full chat sample can be found here: http://www.websharper.com/samples/Chat
Just been playing with this myself. Turns out if you're using Forms Authentication you can read the current HTTPContext from inside RPC methods so you can do something like this:
[<Rpc>]
let protectedMethod () =
match IntelliFactory.WebSharper.Sitelets.UserSession.GetLoggedInUser() with
| Some(username) ->
// User is authenticated... do stuff
()
| None -> failwith "Authentication failed"